docs/default.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         };
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             };
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {Boolean} expanded (true|false) default true
993  * @cfg {String} rheader contet on the right of header
994
995  *     
996  * @constructor
997  * Create a new Container
998  * @param {Object} config The config object
999  */
1000
1001 Roo.bootstrap.Container = function(config){
1002     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1003     
1004     this.addEvents({
1005         // raw events
1006          /**
1007          * @event expand
1008          * After the panel has been expand
1009          * 
1010          * @param {Roo.bootstrap.Container} this
1011          */
1012         "expand" : true,
1013         /**
1014          * @event collapse
1015          * After the panel has been collapsed
1016          * 
1017          * @param {Roo.bootstrap.Container} this
1018          */
1019         "collapse" : true
1020     });
1021 };
1022
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1024     
1025     jumbotron : false,
1026     well: '',
1027     panel : '',
1028     header: '',
1029     footer : '',
1030     sticky: '',
1031     tag : false,
1032     alert : false,
1033     fa: false,
1034     icon : false,
1035     expandable : false,
1036     rheader : '',
1037     expanded : true,
1038   
1039      
1040     getChildContainer : function() {
1041         
1042         if(!this.el){
1043             return false;
1044         }
1045         
1046         if (this.panel.length) {
1047             return this.el.select('.panel-body',true).first();
1048         }
1049         
1050         return this.el;
1051     },
1052     
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : this.tag || 'div',
1058             html : '',
1059             cls : ''
1060         };
1061         if (this.jumbotron) {
1062             cfg.cls = 'jumbotron';
1063         }
1064         
1065         
1066         
1067         // - this is applied by the parent..
1068         //if (this.cls) {
1069         //    cfg.cls = this.cls + '';
1070         //}
1071         
1072         if (this.sticky.length) {
1073             
1074             var bd = Roo.get(document.body);
1075             if (!bd.hasClass('bootstrap-sticky')) {
1076                 bd.addClass('bootstrap-sticky');
1077                 Roo.select('html',true).setStyle('height', '100%');
1078             }
1079              
1080             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1081         }
1082         
1083         
1084         if (this.well.length) {
1085             switch (this.well) {
1086                 case 'lg':
1087                 case 'sm':
1088                     cfg.cls +=' well well-' +this.well;
1089                     break;
1090                 default:
1091                     cfg.cls +=' well';
1092                     break;
1093             }
1094         }
1095         
1096         if (this.hidden) {
1097             cfg.cls += ' hidden';
1098         }
1099         
1100         
1101         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102             cfg.cls +=' alert alert-' + this.alert;
1103         }
1104         
1105         var body = cfg;
1106         
1107         if (this.panel.length) {
1108             cfg.cls += ' panel panel-' + this.panel;
1109             cfg.cn = [];
1110             if (this.header.length) {
1111                 
1112                 var h = [];
1113                 
1114                 if(this.expandable){
1115                     
1116                     cfg.cls = cfg.cls + ' expandable';
1117                     
1118                     h.push({
1119                         tag: 'i',
1120                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1121                     });
1122                     
1123                 }
1124                 
1125                 h.push(
1126                     {
1127                         tag: 'span',
1128                         cls : 'panel-title',
1129                         html : (this.expandable ? '&nbsp;' : '') + this.header
1130                     },
1131                     {
1132                         tag: 'span',
1133                         cls: 'panel-header-right',
1134                         html: this.rheader
1135                     }
1136                 );
1137                 
1138                 cfg.cn.push({
1139                     cls : 'panel-heading',
1140                     style : this.expandable ? 'cursor: pointer' : '',
1141                     cn : h
1142                 });
1143                 
1144             }
1145             
1146             body = false;
1147             cfg.cn.push({
1148                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1149                 html : this.html
1150             });
1151             
1152             
1153             if (this.footer.length) {
1154                 cfg.cn.push({
1155                     cls : 'panel-footer',
1156                     html : this.footer
1157                     
1158                 });
1159             }
1160             
1161         }
1162         
1163         if (body) {
1164             body.html = this.html || cfg.html;
1165             // prefix with the icons..
1166             if (this.fa) {
1167                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1168             }
1169             if (this.icon) {
1170                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1171             }
1172             
1173             
1174         }
1175         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176             cfg.cls =  'container';
1177         }
1178         
1179         return cfg;
1180     },
1181     
1182     initEvents: function() 
1183     {
1184         if(!this.expandable){
1185             return;
1186         }
1187         
1188         var headerEl = this.headerEl();
1189         
1190         if(!headerEl){
1191             return;
1192         }
1193         
1194         headerEl.on('click', this.onToggleClick, this);
1195         
1196     },
1197     
1198     onToggleClick : function()
1199     {
1200         var headerEl = this.headerEl();
1201         
1202         if(!headerEl){
1203             return;
1204         }
1205         
1206         if(this.expanded){
1207             this.collapse();
1208             return;
1209         }
1210         
1211         this.expand();
1212     },
1213     
1214     expand : function()
1215     {
1216         if(this.fireEvent('expand', this)) {
1217             
1218             this.expanded = true;
1219             
1220             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1221             
1222             this.el.select('.panel-body',true).first().removeClass('hide');
1223             
1224             var toggleEl = this.toggleEl();
1225
1226             if(!toggleEl){
1227                 return;
1228             }
1229
1230             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1231         }
1232         
1233     },
1234     
1235     collapse : function()
1236     {
1237         if(this.fireEvent('collapse', this)) {
1238             
1239             this.expanded = false;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242             this.el.select('.panel-body',true).first().addClass('hide');
1243         
1244             var toggleEl = this.toggleEl();
1245
1246             if(!toggleEl){
1247                 return;
1248             }
1249
1250             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1251         }
1252     },
1253     
1254     toggleEl : function()
1255     {
1256         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1257             return;
1258         }
1259         
1260         return this.el.select('.panel-heading .fa',true).first();
1261     },
1262     
1263     headerEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading',true).first()
1270     },
1271     
1272     titleEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-title',true).first();
1279     },
1280     
1281     setTitle : function(v)
1282     {
1283         var titleEl = this.titleEl();
1284         
1285         if(!titleEl){
1286             return;
1287         }
1288         
1289         titleEl.dom.innerHTML = v;
1290     },
1291     
1292     getTitle : function()
1293     {
1294         
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return '';
1299         }
1300         
1301         return titleEl.dom.innerHTML;
1302     },
1303     
1304     setRightTitle : function(v)
1305     {
1306         var t = this.el.select('.panel-header-right',true).first();
1307         
1308         if(!t){
1309             return;
1310         }
1311         
1312         t.dom.innerHTML = v;
1313     }
1314    
1315 });
1316
1317  /*
1318  * - LGPL
1319  *
1320  * image
1321  * 
1322  */
1323
1324
1325 /**
1326  * @class Roo.bootstrap.Img
1327  * @extends Roo.bootstrap.Component
1328  * Bootstrap Img class
1329  * @cfg {Boolean} imgResponsive false | true
1330  * @cfg {String} border rounded | circle | thumbnail
1331  * @cfg {String} src image source
1332  * @cfg {String} alt image alternative text
1333  * @cfg {String} href a tag href
1334  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335  * @cfg {String} xsUrl xs image source
1336  * @cfg {String} smUrl sm image source
1337  * @cfg {String} mdUrl md image source
1338  * @cfg {String} lgUrl lg image source
1339  * 
1340  * @constructor
1341  * Create a new Input
1342  * @param {Object} config The config object
1343  */
1344
1345 Roo.bootstrap.Img = function(config){
1346     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1347     
1348     this.addEvents({
1349         // img events
1350         /**
1351          * @event click
1352          * The img click event for the img.
1353          * @param {Roo.EventObject} e
1354          */
1355         "click" : true
1356     });
1357 };
1358
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1360     
1361     imgResponsive: true,
1362     border: '',
1363     src: '',
1364     href: false,
1365     target: false,
1366     xsUrl: '',
1367     smUrl: '',
1368     mdUrl: '',
1369     lgUrl: '',
1370
1371     getAutoCreate : function()
1372     {   
1373         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374             return this.createSingleImg();
1375         }
1376         
1377         var cfg = {
1378             tag: 'div',
1379             cls: 'roo-image-responsive-group',
1380             cn: []
1381         };
1382         var _this = this;
1383         
1384         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1385             
1386             if(!_this[size + 'Url']){
1387                 return;
1388             }
1389             
1390             var img = {
1391                 tag: 'img',
1392                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393                 html: _this.html || cfg.html,
1394                 src: _this[size + 'Url']
1395             };
1396             
1397             img.cls += ' roo-image-responsive-' + size;
1398             
1399             var s = ['xs', 'sm', 'md', 'lg'];
1400             
1401             s.splice(s.indexOf(size), 1);
1402             
1403             Roo.each(s, function(ss){
1404                 img.cls += ' hidden-' + ss;
1405             });
1406             
1407             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408                 cfg.cls += ' img-' + _this.border;
1409             }
1410             
1411             if(_this.alt){
1412                 cfg.alt = _this.alt;
1413             }
1414             
1415             if(_this.href){
1416                 var a = {
1417                     tag: 'a',
1418                     href: _this.href,
1419                     cn: [
1420                         img
1421                     ]
1422                 };
1423
1424                 if(this.target){
1425                     a.target = _this.target;
1426                 }
1427             }
1428             
1429             cfg.cn.push((_this.href) ? a : img);
1430             
1431         });
1432         
1433         return cfg;
1434     },
1435     
1436     createSingleImg : function()
1437     {
1438         var cfg = {
1439             tag: 'img',
1440             cls: (this.imgResponsive) ? 'img-responsive' : '',
1441             html : null
1442         };
1443         
1444         cfg.html = this.html || cfg.html;
1445         
1446         cfg.src = this.src || cfg.src;
1447         
1448         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449             cfg.cls += ' img-' + this.border;
1450         }
1451         
1452         if(this.alt){
1453             cfg.alt = this.alt;
1454         }
1455         
1456         if(this.href){
1457             var a = {
1458                 tag: 'a',
1459                 href: this.href,
1460                 cn: [
1461                     cfg
1462                 ]
1463             };
1464             
1465             if(this.target){
1466                 a.target = this.target;
1467             }
1468             
1469         }
1470         
1471         return (this.href) ? a : cfg;
1472     },
1473     
1474     initEvents: function() 
1475     {
1476         if(!this.href){
1477             this.el.on('click', this.onClick, this);
1478         }
1479         
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         Roo.log('img onclick');
1485         this.fireEvent('click', this, e);
1486     }
1487    
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Link
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Link Class
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505  * @cfg {String} html the content of the link.
1506  * @cfg {String} anchor name for the anchor link
1507
1508  * @cfg {Boolean} preventDefault (true | false) default false
1509
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Link = function(config){
1517     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1531     
1532     href: false,
1533     target: false,
1534     preventDefault: false,
1535     anchor : false,
1536     alt : false,
1537
1538     getAutoCreate : function()
1539     {
1540         
1541         var cfg = {
1542             tag: 'a'
1543         };
1544         // anchor's do not require html/href...
1545         if (this.anchor === false) {
1546             cfg.html = this.html || '';
1547             cfg.href = this.href || '#';
1548         } else {
1549             cfg.name = this.anchor;
1550             if (this.html !== false) {
1551                 cfg.html = this.html;
1552             }
1553             if (this.href !== false) {
1554                 cfg.href = this.href;
1555             }
1556         }
1557         
1558         if(this.alt !== false){
1559             cfg.alt = this.alt;
1560         }
1561         
1562         
1563         if(this.target !== false) {
1564             cfg.target = this.target;
1565         }
1566         
1567         return cfg;
1568     },
1569     
1570     initEvents: function() {
1571         
1572         if(!this.href || this.preventDefault){
1573             this.el.on('click', this.onClick, this);
1574         }
1575     },
1576     
1577     onClick : function(e)
1578     {
1579         if(this.preventDefault){
1580             e.preventDefault();
1581         }
1582         //Roo.log('img onclick');
1583         this.fireEvent('click', this, e);
1584     }
1585    
1586 });
1587
1588  /*
1589  * - LGPL
1590  *
1591  * header
1592  * 
1593  */
1594
1595 /**
1596  * @class Roo.bootstrap.Header
1597  * @extends Roo.bootstrap.Component
1598  * Bootstrap Header class
1599  * @cfg {String} html content of header
1600  * @cfg {Number} level (1|2|3|4|5|6) default 1
1601  * 
1602  * @constructor
1603  * Create a new Header
1604  * @param {Object} config The config object
1605  */
1606
1607
1608 Roo.bootstrap.Header  = function(config){
1609     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1613     
1614     //href : false,
1615     html : false,
1616     level : 1,
1617     
1618     
1619     
1620     getAutoCreate : function(){
1621         
1622         
1623         
1624         var cfg = {
1625             tag: 'h' + (1 *this.level),
1626             html: this.html || ''
1627         } ;
1628         
1629         return cfg;
1630     }
1631    
1632 });
1633
1634  
1635
1636  /*
1637  * Based on:
1638  * Ext JS Library 1.1.1
1639  * Copyright(c) 2006-2007, Ext JS, LLC.
1640  *
1641  * Originally Released Under LGPL - original licence link has changed is not relivant.
1642  *
1643  * Fork - LGPL
1644  * <script type="text/javascript">
1645  */
1646  
1647 /**
1648  * @class Roo.bootstrap.MenuMgr
1649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1650  * @singleton
1651  */
1652 Roo.bootstrap.MenuMgr = function(){
1653    var menus, active, groups = {}, attached = false, lastShow = new Date();
1654
1655    // private - called when first menu is created
1656    function init(){
1657        menus = {};
1658        active = new Roo.util.MixedCollection();
1659        Roo.get(document).addKeyListener(27, function(){
1660            if(active.length > 0){
1661                hideAll();
1662            }
1663        });
1664    }
1665
1666    // private
1667    function hideAll(){
1668        if(active && active.length > 0){
1669            var c = active.clone();
1670            c.each(function(m){
1671                m.hide();
1672            });
1673        }
1674    }
1675
1676    // private
1677    function onHide(m){
1678        active.remove(m);
1679        if(active.length < 1){
1680            Roo.get(document).un("mouseup", onMouseDown);
1681             
1682            attached = false;
1683        }
1684    }
1685
1686    // private
1687    function onShow(m){
1688        var last = active.last();
1689        lastShow = new Date();
1690        active.add(m);
1691        if(!attached){
1692           Roo.get(document).on("mouseup", onMouseDown);
1693            
1694            attached = true;
1695        }
1696        if(m.parentMenu){
1697           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698           m.parentMenu.activeChild = m;
1699        }else if(last && last.isVisible()){
1700           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1701        }
1702    }
1703
1704    // private
1705    function onBeforeHide(m){
1706        if(m.activeChild){
1707            m.activeChild.hide();
1708        }
1709        if(m.autoHideTimer){
1710            clearTimeout(m.autoHideTimer);
1711            delete m.autoHideTimer;
1712        }
1713    }
1714
1715    // private
1716    function onBeforeShow(m){
1717        var pm = m.parentMenu;
1718        if(!pm && !m.allowOtherMenus){
1719            hideAll();
1720        }else if(pm && pm.activeChild && active != m){
1721            pm.activeChild.hide();
1722        }
1723    }
1724
1725    // private this should really trigger on mouseup..
1726    function onMouseDown(e){
1727         Roo.log("on Mouse Up");
1728         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1729             Roo.log("hideAll");
1730             hideAll();
1731             e.stopEvent();
1732         }
1733         
1734         
1735    }
1736
1737    // private
1738    function onBeforeCheck(mi, state){
1739        if(state){
1740            var g = groups[mi.group];
1741            for(var i = 0, l = g.length; i < l; i++){
1742                if(g[i] != mi){
1743                    g[i].setChecked(false);
1744                }
1745            }
1746        }
1747    }
1748
1749    return {
1750
1751        /**
1752         * Hides all menus that are currently visible
1753         */
1754        hideAll : function(){
1755             hideAll();  
1756        },
1757
1758        // private
1759        register : function(menu){
1760            if(!menus){
1761                init();
1762            }
1763            menus[menu.id] = menu;
1764            menu.on("beforehide", onBeforeHide);
1765            menu.on("hide", onHide);
1766            menu.on("beforeshow", onBeforeShow);
1767            menu.on("show", onShow);
1768            var g = menu.group;
1769            if(g && menu.events["checkchange"]){
1770                if(!groups[g]){
1771                    groups[g] = [];
1772                }
1773                groups[g].push(menu);
1774                menu.on("checkchange", onCheck);
1775            }
1776        },
1777
1778         /**
1779          * Returns a {@link Roo.menu.Menu} object
1780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781          * be used to generate and return a new Menu instance.
1782          */
1783        get : function(menu){
1784            if(typeof menu == "string"){ // menu id
1785                return menus[menu];
1786            }else if(menu.events){  // menu instance
1787                return menu;
1788            }
1789            /*else if(typeof menu.length == 'number'){ // array of menu items?
1790                return new Roo.bootstrap.Menu({items:menu});
1791            }else{ // otherwise, must be a config
1792                return new Roo.bootstrap.Menu(menu);
1793            }
1794            */
1795            return false;
1796        },
1797
1798        // private
1799        unregister : function(menu){
1800            delete menus[menu.id];
1801            menu.un("beforehide", onBeforeHide);
1802            menu.un("hide", onHide);
1803            menu.un("beforeshow", onBeforeShow);
1804            menu.un("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                groups[g].remove(menu);
1808                menu.un("checkchange", onCheck);
1809            }
1810        },
1811
1812        // private
1813        registerCheckable : function(menuItem){
1814            var g = menuItem.group;
1815            if(g){
1816                if(!groups[g]){
1817                    groups[g] = [];
1818                }
1819                groups[g].push(menuItem);
1820                menuItem.on("beforecheckchange", onBeforeCheck);
1821            }
1822        },
1823
1824        // private
1825        unregisterCheckable : function(menuItem){
1826            var g = menuItem.group;
1827            if(g){
1828                groups[g].remove(menuItem);
1829                menuItem.un("beforecheckchange", onBeforeCheck);
1830            }
1831        }
1832    };
1833 }();/*
1834  * - LGPL
1835  *
1836  * menu
1837  * 
1838  */
1839
1840 /**
1841  * @class Roo.bootstrap.Menu
1842  * @extends Roo.bootstrap.Component
1843  * Bootstrap Menu class - container for MenuItems
1844  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1845  * 
1846  * @constructor
1847  * Create a new Menu
1848  * @param {Object} config The config object
1849  */
1850
1851
1852 Roo.bootstrap.Menu = function(config){
1853     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854     if (this.registerMenu) {
1855         Roo.bootstrap.MenuMgr.register(this);
1856     }
1857     this.addEvents({
1858         /**
1859          * @event beforeshow
1860          * Fires before this menu is displayed
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforeshow : true,
1864         /**
1865          * @event beforehide
1866          * Fires before this menu is hidden
1867          * @param {Roo.menu.Menu} this
1868          */
1869         beforehide : true,
1870         /**
1871          * @event show
1872          * Fires after this menu is displayed
1873          * @param {Roo.menu.Menu} this
1874          */
1875         show : true,
1876         /**
1877          * @event hide
1878          * Fires after this menu is hidden
1879          * @param {Roo.menu.Menu} this
1880          */
1881         hide : true,
1882         /**
1883          * @event click
1884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887          * @param {Roo.EventObject} e
1888          */
1889         click : true,
1890         /**
1891          * @event mouseover
1892          * Fires when the mouse is hovering over this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseover : true,
1898         /**
1899          * @event mouseout
1900          * Fires when the mouse exits this menu
1901          * @param {Roo.menu.Menu} this
1902          * @param {Roo.EventObject} e
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          */
1905         mouseout : true,
1906         /**
1907          * @event itemclick
1908          * Fires when a menu item contained in this menu is clicked
1909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910          * @param {Roo.EventObject} e
1911          */
1912         itemclick: true
1913     });
1914     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1915 };
1916
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1918     
1919    /// html : false,
1920     //align : '',
1921     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1922     type: false,
1923     /**
1924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1925      */
1926     registerMenu : true,
1927     
1928     menuItems :false, // stores the menu items..
1929     
1930     hidden:true,
1931     
1932     parentMenu : false,
1933     
1934     getChildContainer : function() {
1935         return this.el;  
1936     },
1937     
1938     getAutoCreate : function(){
1939          
1940         //if (['right'].indexOf(this.align)!==-1) {
1941         //    cfg.cn[1].cls += ' pull-right'
1942         //}
1943         
1944         
1945         var cfg = {
1946             tag : 'ul',
1947             cls : 'dropdown-menu' ,
1948             style : 'z-index:1000'
1949             
1950         };
1951         
1952         if (this.type === 'submenu') {
1953             cfg.cls = 'submenu active';
1954         }
1955         if (this.type === 'treeview') {
1956             cfg.cls = 'treeview-menu';
1957         }
1958         
1959         return cfg;
1960     },
1961     initEvents : function() {
1962         
1963        // Roo.log("ADD event");
1964        // Roo.log(this.triggerEl.dom);
1965         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1966         
1967         this.triggerEl.addClass('dropdown-toggle');
1968         
1969         
1970         
1971         
1972         if (Roo.isTouch) {
1973             this.el.on('touchstart'  , this.onTouch, this);
1974         }
1975         this.el.on('click' , this.onClick, this);
1976
1977         this.el.on("mouseover", this.onMouseOver, this);
1978         this.el.on("mouseout", this.onMouseOut, this);
1979         
1980         
1981     },
1982     findTargetItem : function(e){
1983         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1984         if(!t){
1985             return false;
1986         }
1987         //Roo.log(t);         Roo.log(t.id);
1988         if(t && t.id){
1989             //Roo.log(this.menuitems);
1990             return this.menuitems.get(t.id);
1991             
1992             //return this.items.get(t.menuItemId);
1993         }
1994         
1995         return false;
1996     },
1997     
1998     onTouch : function(e) {
1999         e.stopEvent();
2000         this.onClick(e);
2001     },
2002     
2003     onClick : function(e){
2004         Roo.log("menu.onClick");
2005         var t = this.findTargetItem(e);
2006         if(!t || t.isContainer){
2007             return;
2008         }
2009         Roo.log(e);
2010         /*
2011         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2012             if(t == this.activeItem && t.shouldDeactivate(e)){
2013                 this.activeItem.deactivate();
2014                 delete this.activeItem;
2015                 return;
2016             }
2017             if(t.canActivate){
2018                 this.setActiveItem(t, true);
2019             }
2020             return;
2021             
2022             
2023         }
2024         */
2025        
2026         Roo.log('pass click event');
2027         
2028         t.onClick(e);
2029         
2030         this.fireEvent("click", this, t, e);
2031         
2032         this.hide();
2033     },
2034      onMouseOver : function(e){
2035         var t  = this.findTargetItem(e);
2036         //Roo.log(t);
2037         //if(t){
2038         //    if(t.canActivate && !t.disabled){
2039         //        this.setActiveItem(t, true);
2040         //    }
2041         //}
2042         
2043         this.fireEvent("mouseover", this, e, t);
2044     },
2045     isVisible : function(){
2046         return !this.hidden;
2047     },
2048      onMouseOut : function(e){
2049         var t  = this.findTargetItem(e);
2050         
2051         //if(t ){
2052         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2053         //        this.activeItem.deactivate();
2054         //        delete this.activeItem;
2055         //    }
2056         //}
2057         this.fireEvent("mouseout", this, e, t);
2058     },
2059     
2060     
2061     /**
2062      * Displays this menu relative to another element
2063      * @param {String/HTMLElement/Roo.Element} element The element to align to
2064      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2065      * the element (defaults to this.defaultAlign)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     show : function(el, pos, parentMenu){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         this.fireEvent("beforeshow", this);
2074         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2075     },
2076      /**
2077      * Displays this menu at a specific xy position
2078      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2079      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2080      */
2081     showAt : function(xy, parentMenu, /* private: */_e){
2082         this.parentMenu = parentMenu;
2083         if(!this.el){
2084             this.render();
2085         }
2086         if(_e !== false){
2087             this.fireEvent("beforeshow", this);
2088             //xy = this.el.adjustForConstraints(xy);
2089         }
2090         
2091         //this.el.show();
2092         this.hideMenuItems();
2093         this.hidden = false;
2094         this.triggerEl.addClass('open');
2095         
2096         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2097             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2098         }
2099         
2100         this.el.setXY(xy);
2101         this.focus();
2102         this.fireEvent("show", this);
2103     },
2104     
2105     focus : function(){
2106         return;
2107         if(!this.hidden){
2108             this.doFocus.defer(50, this);
2109         }
2110     },
2111
2112     doFocus : function(){
2113         if(!this.hidden){
2114             this.focusEl.focus();
2115         }
2116     },
2117
2118     /**
2119      * Hides this menu and optionally all parent menus
2120      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2121      */
2122     hide : function(deep){
2123         
2124         this.hideMenuItems();
2125         if(this.el && this.isVisible()){
2126             this.fireEvent("beforehide", this);
2127             if(this.activeItem){
2128                 this.activeItem.deactivate();
2129                 this.activeItem = null;
2130             }
2131             this.triggerEl.removeClass('open');;
2132             this.hidden = true;
2133             this.fireEvent("hide", this);
2134         }
2135         if(deep === true && this.parentMenu){
2136             this.parentMenu.hide(true);
2137         }
2138     },
2139     
2140     onTriggerPress  : function(e)
2141     {
2142         
2143         Roo.log('trigger press');
2144         //Roo.log(e.getTarget());
2145        // Roo.log(this.triggerEl.dom);
2146         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2147             return;
2148         }
2149         
2150         if (this.isVisible()) {
2151             Roo.log('hide');
2152             this.hide();
2153         } else {
2154             Roo.log('show');
2155             this.show(this.triggerEl, false, false);
2156         }
2157         
2158         e.stopEvent();
2159     },
2160     
2161          
2162        
2163     
2164     hideMenuItems : function()
2165     {
2166         //$(backdrop).remove()
2167         Roo.select('.open',true).each(function(aa) {
2168             
2169             aa.removeClass('open');
2170           //var parent = getParent($(this))
2171           //var relatedTarget = { relatedTarget: this }
2172           
2173            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2174           //if (e.isDefaultPrevented()) return
2175            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2176         })
2177     },
2178     addxtypeChild : function (tree, cntr) {
2179         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2180           
2181         this.menuitems.add(comp);
2182         return comp;
2183
2184     },
2185     getEl : function()
2186     {
2187         Roo.log(this.el);
2188         return this.el;
2189     }
2190 });
2191
2192  
2193  /*
2194  * - LGPL
2195  *
2196  * menu item
2197  * 
2198  */
2199
2200
2201 /**
2202  * @class Roo.bootstrap.MenuItem
2203  * @extends Roo.bootstrap.Component
2204  * Bootstrap MenuItem class
2205  * @cfg {String} html the menu label
2206  * @cfg {String} href the link
2207  * @cfg {Boolean} preventDefault (true | false) default true
2208  * @cfg {Boolean} isContainer (true | false) default false
2209  * 
2210  * 
2211  * @constructor
2212  * Create a new MenuItem
2213  * @param {Object} config The config object
2214  */
2215
2216
2217 Roo.bootstrap.MenuItem = function(config){
2218     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2219     this.addEvents({
2220         // raw events
2221         /**
2222          * @event click
2223          * The raw click event for the entire grid.
2224          * @param {Roo.bootstrap.MenuItem} this
2225          * @param {Roo.EventObject} e
2226          */
2227         "click" : true
2228     });
2229 };
2230
2231 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2232     
2233     href : false,
2234     html : false,
2235     preventDefault: true,
2236     isContainer : false,
2237     
2238     getAutoCreate : function(){
2239         
2240         if(this.isContainer){
2241             return {
2242                 tag: 'li',
2243                 cls: 'dropdown-menu-item'
2244             };
2245         }
2246         
2247         var cfg= {
2248             tag: 'li',
2249             cls: 'dropdown-menu-item',
2250             cn: [
2251                     {
2252                         tag : 'a',
2253                         href : '#',
2254                         html : 'Link'
2255                     }
2256                 ]
2257         };
2258         if (this.parent().type == 'treeview') {
2259             cfg.cls = 'treeview-menu';
2260         }
2261         
2262         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2263         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2264         return cfg;
2265     },
2266     
2267     initEvents: function() {
2268         
2269         //this.el.select('a').on('click', this.onClick, this);
2270         
2271     },
2272     onClick : function(e)
2273     {
2274         Roo.log('item on click ');
2275         //if(this.preventDefault){
2276         //    e.preventDefault();
2277         //}
2278         //this.parent().hideMenuItems();
2279         
2280         this.fireEvent('click', this, e);
2281     },
2282     getEl : function()
2283     {
2284         return this.el;
2285     }
2286 });
2287
2288  
2289
2290  /*
2291  * - LGPL
2292  *
2293  * menu separator
2294  * 
2295  */
2296
2297
2298 /**
2299  * @class Roo.bootstrap.MenuSeparator
2300  * @extends Roo.bootstrap.Component
2301  * Bootstrap MenuSeparator class
2302  * 
2303  * @constructor
2304  * Create a new MenuItem
2305  * @param {Object} config The config object
2306  */
2307
2308
2309 Roo.bootstrap.MenuSeparator = function(config){
2310     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2311 };
2312
2313 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2314     
2315     getAutoCreate : function(){
2316         var cfg = {
2317             cls: 'divider',
2318             tag : 'li'
2319         };
2320         
2321         return cfg;
2322     }
2323    
2324 });
2325
2326  
2327
2328  
2329 /*
2330 * Licence: LGPL
2331 */
2332
2333 /**
2334  * @class Roo.bootstrap.Modal
2335  * @extends Roo.bootstrap.Component
2336  * Bootstrap Modal class
2337  * @cfg {String} title Title of dialog
2338  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2339  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2340  * @cfg {Boolean} specificTitle default false
2341  * @cfg {Array} buttons Array of buttons or standard button set..
2342  * @cfg {String} buttonPosition (left|right|center) default right
2343  * @cfg {Boolean} animate default true
2344  * @cfg {Boolean} allow_close default true
2345  * 
2346  * @constructor
2347  * Create a new Modal Dialog
2348  * @param {Object} config The config object
2349  */
2350
2351 Roo.bootstrap.Modal = function(config){
2352     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2353     this.addEvents({
2354         // raw events
2355         /**
2356          * @event btnclick
2357          * The raw btnclick event for the button
2358          * @param {Roo.EventObject} e
2359          */
2360         "btnclick" : true
2361     });
2362     this.buttons = this.buttons || [];
2363      
2364     if (this.tmpl) {
2365         this.tmpl = Roo.factory(this.tmpl);
2366     }
2367     
2368 };
2369
2370 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2371     
2372     title : 'test dialog',
2373    
2374     buttons : false,
2375     
2376     // set on load...
2377      
2378     html: false,
2379     
2380     tmp: false,
2381     
2382     specificTitle: false,
2383     
2384     buttonPosition: 'right',
2385     
2386     allow_close : true,
2387     
2388     animate : true,
2389     
2390     
2391      // private
2392     bodyEl:  false,
2393     footerEl:  false,
2394     titleEl:  false,
2395     closeEl:  false,
2396     
2397     
2398     onRender : function(ct, position)
2399     {
2400         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2401      
2402         if(!this.el){
2403             var cfg = Roo.apply({},  this.getAutoCreate());
2404             cfg.id = Roo.id();
2405             //if(!cfg.name){
2406             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2407             //}
2408             //if (!cfg.name.length) {
2409             //    delete cfg.name;
2410            // }
2411             if (this.cls) {
2412                 cfg.cls += ' ' + this.cls;
2413             }
2414             if (this.style) {
2415                 cfg.style = this.style;
2416             }
2417             this.el = Roo.get(document.body).createChild(cfg, position);
2418         }
2419         //var type = this.el.dom.type;
2420         
2421         
2422         
2423         
2424         if(this.tabIndex !== undefined){
2425             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2426         }
2427         
2428         
2429         this.bodyEl = this.el.select('.modal-body',true).first();
2430         this.closeEl = this.el.select('.modal-header .close', true).first();
2431         this.footerEl = this.el.select('.modal-footer',true).first();
2432         this.titleEl = this.el.select('.modal-title',true).first();
2433         
2434         
2435          
2436         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2437         this.maskEl.enableDisplayMode("block");
2438         this.maskEl.hide();
2439         //this.el.addClass("x-dlg-modal");
2440     
2441         if (this.buttons.length) {
2442             Roo.each(this.buttons, function(bb) {
2443                 var b = Roo.apply({}, bb);
2444                 b.xns = b.xns || Roo.bootstrap;
2445                 b.xtype = b.xtype || 'Button';
2446                 if (typeof(b.listeners) == 'undefined') {
2447                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2448                 }
2449                 
2450                 var btn = Roo.factory(b);
2451                 
2452                 btn.onRender(this.el.select('.modal-footer div').first());
2453                 
2454             },this);
2455         }
2456         // render the children.
2457         var nitems = [];
2458         
2459         if(typeof(this.items) != 'undefined'){
2460             var items = this.items;
2461             delete this.items;
2462
2463             for(var i =0;i < items.length;i++) {
2464                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2465             }
2466         }
2467         
2468         this.items = nitems;
2469         
2470         // where are these used - they used to be body/close/footer
2471         
2472        
2473         this.initEvents();
2474         //this.el.addClass([this.fieldClass, this.cls]);
2475         
2476     },
2477     
2478     getAutoCreate : function(){
2479         
2480         
2481         var bdy = {
2482                 cls : 'modal-body',
2483                 html : this.html || ''
2484         };
2485         
2486         var title = {
2487             tag: 'h4',
2488             cls : 'modal-title',
2489             html : this.title
2490         };
2491         
2492         if(this.specificTitle){
2493             title = this.title;
2494             
2495         };
2496         
2497         var header = [];
2498         if (this.allow_close) {
2499             header.push({
2500                 tag: 'button',
2501                 cls : 'close',
2502                 html : '&times'
2503             });
2504         }
2505         header.push(title);
2506         
2507         var modal = {
2508             cls: "modal",
2509             style : 'display: none',
2510             cn : [
2511                 {
2512                     cls: "modal-dialog",
2513                     cn : [
2514                         {
2515                             cls : "modal-content",
2516                             cn : [
2517                                 {
2518                                     cls : 'modal-header',
2519                                     cn : header
2520                                 },
2521                                 bdy,
2522                                 {
2523                                     cls : 'modal-footer',
2524                                     cn : [
2525                                         {
2526                                             tag: 'div',
2527                                             cls: 'btn-' + this.buttonPosition
2528                                         }
2529                                     ]
2530                                     
2531                                 }
2532                                 
2533                                 
2534                             ]
2535                             
2536                         }
2537                     ]
2538                         
2539                 }
2540             ]
2541         };
2542         
2543         if(this.animate){
2544             modal.cls += ' fade';
2545         }
2546         
2547         return modal;
2548           
2549     },
2550     getChildContainer : function() {
2551          
2552          return this.bodyEl;
2553         
2554     },
2555     getButtonContainer : function() {
2556          return this.el.select('.modal-footer div',true).first();
2557         
2558     },
2559     initEvents : function()
2560     {
2561         if (this.allow_close) {
2562             this.closeEl.on('click', this.hide, this);
2563         }
2564         
2565         var _this = this;
2566         
2567         window.addEventListener("resize", function() { _this.resize(); } );
2568
2569     },
2570     
2571     resize : function()
2572     {
2573         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2574     },
2575     
2576     show : function() {
2577         
2578         if (!this.rendered) {
2579             this.render();
2580         }
2581         
2582         this.el.setStyle('display', 'block');
2583         
2584         if(this.animate){
2585             var _this = this;
2586             (function(){ _this.el.addClass('in'); }).defer(50);
2587         }else{
2588             this.el.addClass('in');
2589         }
2590         
2591         // not sure how we can show data in here.. 
2592         //if (this.tmpl) {
2593         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2594         //}
2595         
2596         Roo.get(document.body).addClass("x-body-masked");
2597         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2598         this.maskEl.show();
2599         this.el.setStyle('zIndex', '10001');
2600        
2601         this.fireEvent('show', this);
2602         
2603         
2604     },
2605     hide : function()
2606     {
2607         this.maskEl.hide();
2608         Roo.get(document.body).removeClass("x-body-masked");
2609         this.el.removeClass('in');
2610         
2611         if(this.animate){
2612             var _this = this;
2613             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2614         }else{
2615             this.el.setStyle('display', 'none');
2616         }
2617         
2618         this.fireEvent('hide', this);
2619     },
2620     
2621     addButton : function(str, cb)
2622     {
2623          
2624         
2625         var b = Roo.apply({}, { html : str } );
2626         b.xns = b.xns || Roo.bootstrap;
2627         b.xtype = b.xtype || 'Button';
2628         if (typeof(b.listeners) == 'undefined') {
2629             b.listeners = { click : cb.createDelegate(this)  };
2630         }
2631         
2632         var btn = Roo.factory(b);
2633            
2634         btn.onRender(this.el.select('.modal-footer div').first());
2635         
2636         return btn;   
2637        
2638     },
2639     
2640     setDefaultButton : function(btn)
2641     {
2642         //this.el.select('.modal-footer').()
2643     },
2644     resizeTo: function(w,h)
2645     {
2646         // skip..
2647     },
2648     setContentSize  : function(w, h)
2649     {
2650         
2651     },
2652     onButtonClick: function(btn,e)
2653     {
2654         //Roo.log([a,b,c]);
2655         this.fireEvent('btnclick', btn.name, e);
2656     },
2657      /**
2658      * Set the title of the Dialog
2659      * @param {String} str new Title
2660      */
2661     setTitle: function(str) {
2662         this.titleEl.dom.innerHTML = str;    
2663     },
2664     /**
2665      * Set the body of the Dialog
2666      * @param {String} str new Title
2667      */
2668     setBody: function(str) {
2669         this.bodyEl.dom.innerHTML = str;    
2670     },
2671     /**
2672      * Set the body of the Dialog using the template
2673      * @param {Obj} data - apply this data to the template and replace the body contents.
2674      */
2675     applyBody: function(obj)
2676     {
2677         if (!this.tmpl) {
2678             Roo.log("Error - using apply Body without a template");
2679             //code
2680         }
2681         this.tmpl.overwrite(this.bodyEl, obj);
2682     }
2683     
2684 });
2685
2686
2687 Roo.apply(Roo.bootstrap.Modal,  {
2688     /**
2689          * Button config that displays a single OK button
2690          * @type Object
2691          */
2692         OK :  [{
2693             name : 'ok',
2694             weight : 'primary',
2695             html : 'OK'
2696         }], 
2697         /**
2698          * Button config that displays Yes and No buttons
2699          * @type Object
2700          */
2701         YESNO : [
2702             {
2703                 name  : 'no',
2704                 html : 'No'
2705             },
2706             {
2707                 name  :'yes',
2708                 weight : 'primary',
2709                 html : 'Yes'
2710             }
2711         ],
2712         
2713         /**
2714          * Button config that displays OK and Cancel buttons
2715          * @type Object
2716          */
2717         OKCANCEL : [
2718             {
2719                name : 'cancel',
2720                 html : 'Cancel'
2721             },
2722             {
2723                 name : 'ok',
2724                 weight : 'primary',
2725                 html : 'OK'
2726             }
2727         ],
2728         /**
2729          * Button config that displays Yes, No and Cancel buttons
2730          * @type Object
2731          */
2732         YESNOCANCEL : [
2733             {
2734                 name : 'yes',
2735                 weight : 'primary',
2736                 html : 'Yes'
2737             },
2738             {
2739                 name : 'no',
2740                 html : 'No'
2741             },
2742             {
2743                 name : 'cancel',
2744                 html : 'Cancel'
2745             }
2746         ]
2747 });
2748  
2749  /*
2750  * - LGPL
2751  *
2752  * messagebox - can be used as a replace
2753  * 
2754  */
2755 /**
2756  * @class Roo.MessageBox
2757  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2758  * Example usage:
2759  *<pre><code>
2760 // Basic alert:
2761 Roo.Msg.alert('Status', 'Changes saved successfully.');
2762
2763 // Prompt for user data:
2764 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2765     if (btn == 'ok'){
2766         // process text value...
2767     }
2768 });
2769
2770 // Show a dialog using config options:
2771 Roo.Msg.show({
2772    title:'Save Changes?',
2773    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2774    buttons: Roo.Msg.YESNOCANCEL,
2775    fn: processResult,
2776    animEl: 'elId'
2777 });
2778 </code></pre>
2779  * @singleton
2780  */
2781 Roo.bootstrap.MessageBox = function(){
2782     var dlg, opt, mask, waitTimer;
2783     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2784     var buttons, activeTextEl, bwidth;
2785
2786     
2787     // private
2788     var handleButton = function(button){
2789         dlg.hide();
2790         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2791     };
2792
2793     // private
2794     var handleHide = function(){
2795         if(opt && opt.cls){
2796             dlg.el.removeClass(opt.cls);
2797         }
2798         //if(waitTimer){
2799         //    Roo.TaskMgr.stop(waitTimer);
2800         //    waitTimer = null;
2801         //}
2802     };
2803
2804     // private
2805     var updateButtons = function(b){
2806         var width = 0;
2807         if(!b){
2808             buttons["ok"].hide();
2809             buttons["cancel"].hide();
2810             buttons["yes"].hide();
2811             buttons["no"].hide();
2812             //dlg.footer.dom.style.display = 'none';
2813             return width;
2814         }
2815         dlg.footerEl.dom.style.display = '';
2816         for(var k in buttons){
2817             if(typeof buttons[k] != "function"){
2818                 if(b[k]){
2819                     buttons[k].show();
2820                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2821                     width += buttons[k].el.getWidth()+15;
2822                 }else{
2823                     buttons[k].hide();
2824                 }
2825             }
2826         }
2827         return width;
2828     };
2829
2830     // private
2831     var handleEsc = function(d, k, e){
2832         if(opt && opt.closable !== false){
2833             dlg.hide();
2834         }
2835         if(e){
2836             e.stopEvent();
2837         }
2838     };
2839
2840     return {
2841         /**
2842          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2843          * @return {Roo.BasicDialog} The BasicDialog element
2844          */
2845         getDialog : function(){
2846            if(!dlg){
2847                 dlg = new Roo.bootstrap.Modal( {
2848                     //draggable: true,
2849                     //resizable:false,
2850                     //constraintoviewport:false,
2851                     //fixedcenter:true,
2852                     //collapsible : false,
2853                     //shim:true,
2854                     //modal: true,
2855                   //  width:400,
2856                   //  height:100,
2857                     //buttonAlign:"center",
2858                     closeClick : function(){
2859                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2860                             handleButton("no");
2861                         }else{
2862                             handleButton("cancel");
2863                         }
2864                     }
2865                 });
2866                 dlg.render();
2867                 dlg.on("hide", handleHide);
2868                 mask = dlg.mask;
2869                 //dlg.addKeyListener(27, handleEsc);
2870                 buttons = {};
2871                 this.buttons = buttons;
2872                 var bt = this.buttonText;
2873                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2874                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2875                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2876                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2877                 Roo.log(buttons)
2878                 bodyEl = dlg.bodyEl.createChild({
2879
2880                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2881                         '<textarea class="roo-mb-textarea"></textarea>' +
2882                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2883                 });
2884                 msgEl = bodyEl.dom.firstChild;
2885                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2886                 textboxEl.enableDisplayMode();
2887                 textboxEl.addKeyListener([10,13], function(){
2888                     if(dlg.isVisible() && opt && opt.buttons){
2889                         if(opt.buttons.ok){
2890                             handleButton("ok");
2891                         }else if(opt.buttons.yes){
2892                             handleButton("yes");
2893                         }
2894                     }
2895                 });
2896                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2897                 textareaEl.enableDisplayMode();
2898                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2899                 progressEl.enableDisplayMode();
2900                 var pf = progressEl.dom.firstChild;
2901                 if (pf) {
2902                     pp = Roo.get(pf.firstChild);
2903                     pp.setHeight(pf.offsetHeight);
2904                 }
2905                 
2906             }
2907             return dlg;
2908         },
2909
2910         /**
2911          * Updates the message box body text
2912          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2913          * the XHTML-compliant non-breaking space character '&amp;#160;')
2914          * @return {Roo.MessageBox} This message box
2915          */
2916         updateText : function(text){
2917             if(!dlg.isVisible() && !opt.width){
2918                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2919             }
2920             msgEl.innerHTML = text || '&#160;';
2921       
2922             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2923             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2924             var w = Math.max(
2925                     Math.min(opt.width || cw , this.maxWidth), 
2926                     Math.max(opt.minWidth || this.minWidth, bwidth)
2927             );
2928             if(opt.prompt){
2929                 activeTextEl.setWidth(w);
2930             }
2931             if(dlg.isVisible()){
2932                 dlg.fixedcenter = false;
2933             }
2934             // to big, make it scroll. = But as usual stupid IE does not support
2935             // !important..
2936             
2937             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2938                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2939                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2940             } else {
2941                 bodyEl.dom.style.height = '';
2942                 bodyEl.dom.style.overflowY = '';
2943             }
2944             if (cw > w) {
2945                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2946             } else {
2947                 bodyEl.dom.style.overflowX = '';
2948             }
2949             
2950             dlg.setContentSize(w, bodyEl.getHeight());
2951             if(dlg.isVisible()){
2952                 dlg.fixedcenter = true;
2953             }
2954             return this;
2955         },
2956
2957         /**
2958          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2959          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2960          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2961          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2962          * @return {Roo.MessageBox} This message box
2963          */
2964         updateProgress : function(value, text){
2965             if(text){
2966                 this.updateText(text);
2967             }
2968             if (pp) { // weird bug on my firefox - for some reason this is not defined
2969                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2970             }
2971             return this;
2972         },        
2973
2974         /**
2975          * Returns true if the message box is currently displayed
2976          * @return {Boolean} True if the message box is visible, else false
2977          */
2978         isVisible : function(){
2979             return dlg && dlg.isVisible();  
2980         },
2981
2982         /**
2983          * Hides the message box if it is displayed
2984          */
2985         hide : function(){
2986             if(this.isVisible()){
2987                 dlg.hide();
2988             }  
2989         },
2990
2991         /**
2992          * Displays a new message box, or reinitializes an existing message box, based on the config options
2993          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2994          * The following config object properties are supported:
2995          * <pre>
2996 Property    Type             Description
2997 ----------  ---------------  ------------------------------------------------------------------------------------
2998 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2999                                    closes (defaults to undefined)
3000 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3001                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3002 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3003                                    progress and wait dialogs will ignore this property and always hide the
3004                                    close button as they can only be closed programmatically.
3005 cls               String           A custom CSS class to apply to the message box element
3006 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3007                                    displayed (defaults to 75)
3008 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3009                                    function will be btn (the name of the button that was clicked, if applicable,
3010                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3011                                    Progress and wait dialogs will ignore this option since they do not respond to
3012                                    user actions and can only be closed programmatically, so any required function
3013                                    should be called by the same code after it closes the dialog.
3014 icon              String           A CSS class that provides a background image to be used as an icon for
3015                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3016 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3017 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3018 modal             Boolean          False to allow user interaction with the page while the message box is
3019                                    displayed (defaults to true)
3020 msg               String           A string that will replace the existing message box body text (defaults
3021                                    to the XHTML-compliant non-breaking space character '&#160;')
3022 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3023 progress          Boolean          True to display a progress bar (defaults to false)
3024 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3025 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3026 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3027 title             String           The title text
3028 value             String           The string value to set into the active textbox element if displayed
3029 wait              Boolean          True to display a progress bar (defaults to false)
3030 width             Number           The width of the dialog in pixels
3031 </pre>
3032          *
3033          * Example usage:
3034          * <pre><code>
3035 Roo.Msg.show({
3036    title: 'Address',
3037    msg: 'Please enter your address:',
3038    width: 300,
3039    buttons: Roo.MessageBox.OKCANCEL,
3040    multiline: true,
3041    fn: saveAddress,
3042    animEl: 'addAddressBtn'
3043 });
3044 </code></pre>
3045          * @param {Object} config Configuration options
3046          * @return {Roo.MessageBox} This message box
3047          */
3048         show : function(options)
3049         {
3050             
3051             // this causes nightmares if you show one dialog after another
3052             // especially on callbacks..
3053              
3054             if(this.isVisible()){
3055                 
3056                 this.hide();
3057                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3058                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3059                 Roo.log("New Dialog Message:" +  options.msg )
3060                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3061                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3062                 
3063             }
3064             var d = this.getDialog();
3065             opt = options;
3066             d.setTitle(opt.title || "&#160;");
3067             d.closeEl.setDisplayed(opt.closable !== false);
3068             activeTextEl = textboxEl;
3069             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3070             if(opt.prompt){
3071                 if(opt.multiline){
3072                     textboxEl.hide();
3073                     textareaEl.show();
3074                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3075                         opt.multiline : this.defaultTextHeight);
3076                     activeTextEl = textareaEl;
3077                 }else{
3078                     textboxEl.show();
3079                     textareaEl.hide();
3080                 }
3081             }else{
3082                 textboxEl.hide();
3083                 textareaEl.hide();
3084             }
3085             progressEl.setDisplayed(opt.progress === true);
3086             this.updateProgress(0);
3087             activeTextEl.dom.value = opt.value || "";
3088             if(opt.prompt){
3089                 dlg.setDefaultButton(activeTextEl);
3090             }else{
3091                 var bs = opt.buttons;
3092                 var db = null;
3093                 if(bs && bs.ok){
3094                     db = buttons["ok"];
3095                 }else if(bs && bs.yes){
3096                     db = buttons["yes"];
3097                 }
3098                 dlg.setDefaultButton(db);
3099             }
3100             bwidth = updateButtons(opt.buttons);
3101             this.updateText(opt.msg);
3102             if(opt.cls){
3103                 d.el.addClass(opt.cls);
3104             }
3105             d.proxyDrag = opt.proxyDrag === true;
3106             d.modal = opt.modal !== false;
3107             d.mask = opt.modal !== false ? mask : false;
3108             if(!d.isVisible()){
3109                 // force it to the end of the z-index stack so it gets a cursor in FF
3110                 document.body.appendChild(dlg.el.dom);
3111                 d.animateTarget = null;
3112                 d.show(options.animEl);
3113             }
3114             return this;
3115         },
3116
3117         /**
3118          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3119          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3120          * and closing the message box when the process is complete.
3121          * @param {String} title The title bar text
3122          * @param {String} msg The message box body text
3123          * @return {Roo.MessageBox} This message box
3124          */
3125         progress : function(title, msg){
3126             this.show({
3127                 title : title,
3128                 msg : msg,
3129                 buttons: false,
3130                 progress:true,
3131                 closable:false,
3132                 minWidth: this.minProgressWidth,
3133                 modal : true
3134             });
3135             return this;
3136         },
3137
3138         /**
3139          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3140          * If a callback function is passed it will be called after the user clicks the button, and the
3141          * id of the button that was clicked will be passed as the only parameter to the callback
3142          * (could also be the top-right close button).
3143          * @param {String} title The title bar text
3144          * @param {String} msg The message box body text
3145          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3146          * @param {Object} scope (optional) The scope of the callback function
3147          * @return {Roo.MessageBox} This message box
3148          */
3149         alert : function(title, msg, fn, scope){
3150             this.show({
3151                 title : title,
3152                 msg : msg,
3153                 buttons: this.OK,
3154                 fn: fn,
3155                 scope : scope,
3156                 modal : true
3157             });
3158             return this;
3159         },
3160
3161         /**
3162          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3163          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3164          * You are responsible for closing the message box when the process is complete.
3165          * @param {String} msg The message box body text
3166          * @param {String} title (optional) The title bar text
3167          * @return {Roo.MessageBox} This message box
3168          */
3169         wait : function(msg, title){
3170             this.show({
3171                 title : title,
3172                 msg : msg,
3173                 buttons: false,
3174                 closable:false,
3175                 progress:true,
3176                 modal:true,
3177                 width:300,
3178                 wait:true
3179             });
3180             waitTimer = Roo.TaskMgr.start({
3181                 run: function(i){
3182                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3183                 },
3184                 interval: 1000
3185             });
3186             return this;
3187         },
3188
3189         /**
3190          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3191          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3192          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3193          * @param {String} title The title bar text
3194          * @param {String} msg The message box body text
3195          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3196          * @param {Object} scope (optional) The scope of the callback function
3197          * @return {Roo.MessageBox} This message box
3198          */
3199         confirm : function(title, msg, fn, scope){
3200             this.show({
3201                 title : title,
3202                 msg : msg,
3203                 buttons: this.YESNO,
3204                 fn: fn,
3205                 scope : scope,
3206                 modal : true
3207             });
3208             return this;
3209         },
3210
3211         /**
3212          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3213          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3214          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3215          * (could also be the top-right close button) and the text that was entered will be passed as the two
3216          * parameters to the callback.
3217          * @param {String} title The title bar text
3218          * @param {String} msg The message box body text
3219          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3220          * @param {Object} scope (optional) The scope of the callback function
3221          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3222          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3223          * @return {Roo.MessageBox} This message box
3224          */
3225         prompt : function(title, msg, fn, scope, multiline){
3226             this.show({
3227                 title : title,
3228                 msg : msg,
3229                 buttons: this.OKCANCEL,
3230                 fn: fn,
3231                 minWidth:250,
3232                 scope : scope,
3233                 prompt:true,
3234                 multiline: multiline,
3235                 modal : true
3236             });
3237             return this;
3238         },
3239
3240         /**
3241          * Button config that displays a single OK button
3242          * @type Object
3243          */
3244         OK : {ok:true},
3245         /**
3246          * Button config that displays Yes and No buttons
3247          * @type Object
3248          */
3249         YESNO : {yes:true, no:true},
3250         /**
3251          * Button config that displays OK and Cancel buttons
3252          * @type Object
3253          */
3254         OKCANCEL : {ok:true, cancel:true},
3255         /**
3256          * Button config that displays Yes, No and Cancel buttons
3257          * @type Object
3258          */
3259         YESNOCANCEL : {yes:true, no:true, cancel:true},
3260
3261         /**
3262          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3263          * @type Number
3264          */
3265         defaultTextHeight : 75,
3266         /**
3267          * The maximum width in pixels of the message box (defaults to 600)
3268          * @type Number
3269          */
3270         maxWidth : 600,
3271         /**
3272          * The minimum width in pixels of the message box (defaults to 100)
3273          * @type Number
3274          */
3275         minWidth : 100,
3276         /**
3277          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3278          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3279          * @type Number
3280          */
3281         minProgressWidth : 250,
3282         /**
3283          * An object containing the default button text strings that can be overriden for localized language support.
3284          * Supported properties are: ok, cancel, yes and no.
3285          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3286          * @type Object
3287          */
3288         buttonText : {
3289             ok : "OK",
3290             cancel : "Cancel",
3291             yes : "Yes",
3292             no : "No"
3293         }
3294     };
3295 }();
3296
3297 /**
3298  * Shorthand for {@link Roo.MessageBox}
3299  */
3300 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3301 Roo.Msg = Roo.Msg || Roo.MessageBox;
3302 /*
3303  * - LGPL
3304  *
3305  * navbar
3306  * 
3307  */
3308
3309 /**
3310  * @class Roo.bootstrap.Navbar
3311  * @extends Roo.bootstrap.Component
3312  * Bootstrap Navbar class
3313
3314  * @constructor
3315  * Create a new Navbar
3316  * @param {Object} config The config object
3317  */
3318
3319
3320 Roo.bootstrap.Navbar = function(config){
3321     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3322     
3323 };
3324
3325 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3326     
3327     
3328    
3329     // private
3330     navItems : false,
3331     loadMask : false,
3332     
3333     
3334     getAutoCreate : function(){
3335         
3336         
3337         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3338         
3339     },
3340     
3341     initEvents :function ()
3342     {
3343         //Roo.log(this.el.select('.navbar-toggle',true));
3344         this.el.select('.navbar-toggle',true).on('click', function() {
3345            // Roo.log('click');
3346             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3347         }, this);
3348         
3349         var mark = {
3350             tag: "div",
3351             cls:"x-dlg-mask"
3352         };
3353         
3354         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3355         
3356         var size = this.el.getSize();
3357         this.maskEl.setSize(size.width, size.height);
3358         this.maskEl.enableDisplayMode("block");
3359         this.maskEl.hide();
3360         
3361         if(this.loadMask){
3362             this.maskEl.show();
3363         }
3364     },
3365     
3366     
3367     getChildContainer : function()
3368     {
3369         if (this.el.select('.collapse').getCount()) {
3370             return this.el.select('.collapse',true).first();
3371         }
3372         
3373         return this.el;
3374     },
3375     
3376     mask : function()
3377     {
3378         this.maskEl.show();
3379     },
3380     
3381     unmask : function()
3382     {
3383         this.maskEl.hide();
3384     } 
3385     
3386     
3387     
3388     
3389 });
3390
3391
3392
3393  
3394
3395  /*
3396  * - LGPL
3397  *
3398  * navbar
3399  * 
3400  */
3401
3402 /**
3403  * @class Roo.bootstrap.NavSimplebar
3404  * @extends Roo.bootstrap.Navbar
3405  * Bootstrap Sidebar class
3406  *
3407  * @cfg {Boolean} inverse is inverted color
3408  * 
3409  * @cfg {String} type (nav | pills | tabs)
3410  * @cfg {Boolean} arrangement stacked | justified
3411  * @cfg {String} align (left | right) alignment
3412  * 
3413  * @cfg {Boolean} main (true|false) main nav bar? default false
3414  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3415  * 
3416  * @cfg {String} tag (header|footer|nav|div) default is nav 
3417
3418  * 
3419  * 
3420  * 
3421  * @constructor
3422  * Create a new Sidebar
3423  * @param {Object} config The config object
3424  */
3425
3426
3427 Roo.bootstrap.NavSimplebar = function(config){
3428     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3429 };
3430
3431 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3432     
3433     inverse: false,
3434     
3435     type: false,
3436     arrangement: '',
3437     align : false,
3438     
3439     
3440     
3441     main : false,
3442     
3443     
3444     tag : false,
3445     
3446     
3447     getAutoCreate : function(){
3448         
3449         
3450         var cfg = {
3451             tag : this.tag || 'div',
3452             cls : 'navbar'
3453         };
3454           
3455         
3456         cfg.cn = [
3457             {
3458                 cls: 'nav',
3459                 tag : 'ul'
3460             }
3461         ];
3462         
3463          
3464         this.type = this.type || 'nav';
3465         if (['tabs','pills'].indexOf(this.type)!==-1) {
3466             cfg.cn[0].cls += ' nav-' + this.type
3467         
3468         
3469         } else {
3470             if (this.type!=='nav') {
3471                 Roo.log('nav type must be nav/tabs/pills')
3472             }
3473             cfg.cn[0].cls += ' navbar-nav'
3474         }
3475         
3476         
3477         
3478         
3479         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3480             cfg.cn[0].cls += ' nav-' + this.arrangement;
3481         }
3482         
3483         
3484         if (this.align === 'right') {
3485             cfg.cn[0].cls += ' navbar-right';
3486         }
3487         
3488         if (this.inverse) {
3489             cfg.cls += ' navbar-inverse';
3490             
3491         }
3492         
3493         
3494         return cfg;
3495     
3496         
3497     }
3498     
3499     
3500     
3501 });
3502
3503
3504
3505  
3506
3507  
3508        /*
3509  * - LGPL
3510  *
3511  * navbar
3512  * 
3513  */
3514
3515 /**
3516  * @class Roo.bootstrap.NavHeaderbar
3517  * @extends Roo.bootstrap.NavSimplebar
3518  * Bootstrap Sidebar class
3519  *
3520  * @cfg {String} brand what is brand
3521  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3522  * @cfg {String} brand_href href of the brand
3523  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3524  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3525  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3526  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3527  * 
3528  * @constructor
3529  * Create a new Sidebar
3530  * @param {Object} config The config object
3531  */
3532
3533
3534 Roo.bootstrap.NavHeaderbar = function(config){
3535     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3536       
3537 };
3538
3539 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3540     
3541     position: '',
3542     brand: '',
3543     brand_href: false,
3544     srButton : true,
3545     autohide : false,
3546     desktopCenter : false,
3547    
3548     
3549     getAutoCreate : function(){
3550         
3551         var   cfg = {
3552             tag: this.nav || 'nav',
3553             cls: 'navbar',
3554             role: 'navigation',
3555             cn: []
3556         };
3557         
3558         var cn = cfg.cn;
3559         if (this.desktopCenter) {
3560             cn.push({cls : 'container', cn : []});
3561             cn = cn[0].cn;
3562         }
3563         
3564         if(this.srButton){
3565             cn.push({
3566                 tag: 'div',
3567                 cls: 'navbar-header',
3568                 cn: [
3569                     {
3570                         tag: 'button',
3571                         type: 'button',
3572                         cls: 'navbar-toggle',
3573                         'data-toggle': 'collapse',
3574                         cn: [
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'sr-only',
3578                                 html: 'Toggle navigation'
3579                             },
3580                             {
3581                                 tag: 'span',
3582                                 cls: 'icon-bar'
3583                             },
3584                             {
3585                                 tag: 'span',
3586                                 cls: 'icon-bar'
3587                             },
3588                             {
3589                                 tag: 'span',
3590                                 cls: 'icon-bar'
3591                             }
3592                         ]
3593                     }
3594                 ]
3595             });
3596         }
3597         
3598         cn.push({
3599             tag: 'div',
3600             cls: 'collapse navbar-collapse',
3601             cn : []
3602         });
3603         
3604         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3605         
3606         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3607             cfg.cls += ' navbar-' + this.position;
3608             
3609             // tag can override this..
3610             
3611             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3612         }
3613         
3614         if (this.brand !== '') {
3615             cn[0].cn.push({
3616                 tag: 'a',
3617                 href: this.brand_href ? this.brand_href : '#',
3618                 cls: 'navbar-brand',
3619                 cn: [
3620                 this.brand
3621                 ]
3622             });
3623         }
3624         
3625         if(this.main){
3626             cfg.cls += ' main-nav';
3627         }
3628         
3629         
3630         return cfg;
3631
3632         
3633     },
3634     getHeaderChildContainer : function()
3635     {
3636         if (this.el.select('.navbar-header').getCount()) {
3637             return this.el.select('.navbar-header',true).first();
3638         }
3639         
3640         return this.getChildContainer();
3641     },
3642     
3643     
3644     initEvents : function()
3645     {
3646         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3647         
3648         if (this.autohide) {
3649             
3650             var prevScroll = 0;
3651             var ft = this.el;
3652             
3653             Roo.get(document).on('scroll',function(e) {
3654                 var ns = Roo.get(document).getScroll().top;
3655                 var os = prevScroll;
3656                 prevScroll = ns;
3657                 
3658                 if(ns > os){
3659                     ft.removeClass('slideDown');
3660                     ft.addClass('slideUp');
3661                     return;
3662                 }
3663                 ft.removeClass('slideUp');
3664                 ft.addClass('slideDown');
3665                  
3666               
3667           },this);
3668         }
3669     }    
3670     
3671 });
3672
3673
3674
3675  
3676
3677  /*
3678  * - LGPL
3679  *
3680  * navbar
3681  * 
3682  */
3683
3684 /**
3685  * @class Roo.bootstrap.NavSidebar
3686  * @extends Roo.bootstrap.Navbar
3687  * Bootstrap Sidebar class
3688  * 
3689  * @constructor
3690  * Create a new Sidebar
3691  * @param {Object} config The config object
3692  */
3693
3694
3695 Roo.bootstrap.NavSidebar = function(config){
3696     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3697 };
3698
3699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3700     
3701     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3702     
3703     getAutoCreate : function(){
3704         
3705         
3706         return  {
3707             tag: 'div',
3708             cls: 'sidebar sidebar-nav'
3709         };
3710     
3711         
3712     }
3713     
3714     
3715     
3716 });
3717
3718
3719
3720  
3721
3722  /*
3723  * - LGPL
3724  *
3725  * nav group
3726  * 
3727  */
3728
3729 /**
3730  * @class Roo.bootstrap.NavGroup
3731  * @extends Roo.bootstrap.Component
3732  * Bootstrap NavGroup class
3733  * @cfg {String} align (left|right)
3734  * @cfg {Boolean} inverse
3735  * @cfg {String} type (nav|pills|tab) default nav
3736  * @cfg {String} navId - reference Id for navbar.
3737
3738  * 
3739  * @constructor
3740  * Create a new nav group
3741  * @param {Object} config The config object
3742  */
3743
3744 Roo.bootstrap.NavGroup = function(config){
3745     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3746     this.navItems = [];
3747    
3748     Roo.bootstrap.NavGroup.register(this);
3749      this.addEvents({
3750         /**
3751              * @event changed
3752              * Fires when the active item changes
3753              * @param {Roo.bootstrap.NavGroup} this
3754              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3755              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3756          */
3757         'changed': true
3758      });
3759     
3760 };
3761
3762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3763     
3764     align: '',
3765     inverse: false,
3766     form: false,
3767     type: 'nav',
3768     navId : '',
3769     // private
3770     
3771     navItems : false, 
3772     
3773     getAutoCreate : function()
3774     {
3775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3776         
3777         cfg = {
3778             tag : 'ul',
3779             cls: 'nav' 
3780         };
3781         
3782         if (['tabs','pills'].indexOf(this.type)!==-1) {
3783             cfg.cls += ' nav-' + this.type
3784         } else {
3785             if (this.type!=='nav') {
3786                 Roo.log('nav type must be nav/tabs/pills')
3787             }
3788             cfg.cls += ' navbar-nav'
3789         }
3790         
3791         if (this.parent().sidebar) {
3792             cfg = {
3793                 tag: 'ul',
3794                 cls: 'dashboard-menu sidebar-menu'
3795             };
3796             
3797             return cfg;
3798         }
3799         
3800         if (this.form === true) {
3801             cfg = {
3802                 tag: 'form',
3803                 cls: 'navbar-form'
3804             };
3805             
3806             if (this.align === 'right') {
3807                 cfg.cls += ' navbar-right';
3808             } else {
3809                 cfg.cls += ' navbar-left';
3810             }
3811         }
3812         
3813         if (this.align === 'right') {
3814             cfg.cls += ' navbar-right';
3815         }
3816         
3817         if (this.inverse) {
3818             cfg.cls += ' navbar-inverse';
3819             
3820         }
3821         
3822         
3823         return cfg;
3824     },
3825     /**
3826     * sets the active Navigation item
3827     * @param {Roo.bootstrap.NavItem} the new current navitem
3828     */
3829     setActiveItem : function(item)
3830     {
3831         var prev = false;
3832         Roo.each(this.navItems, function(v){
3833             if (v == item) {
3834                 return ;
3835             }
3836             if (v.isActive()) {
3837                 v.setActive(false, true);
3838                 prev = v;
3839                 
3840             }
3841             
3842         });
3843
3844         item.setActive(true, true);
3845         this.fireEvent('changed', this, item, prev);
3846         
3847         
3848     },
3849     /**
3850     * gets the active Navigation item
3851     * @return {Roo.bootstrap.NavItem} the current navitem
3852     */
3853     getActive : function()
3854     {
3855         
3856         var prev = false;
3857         Roo.each(this.navItems, function(v){
3858             
3859             if (v.isActive()) {
3860                 prev = v;
3861                 
3862             }
3863             
3864         });
3865         return prev;
3866     },
3867     
3868     indexOfNav : function()
3869     {
3870         
3871         var prev = false;
3872         Roo.each(this.navItems, function(v,i){
3873             
3874             if (v.isActive()) {
3875                 prev = i;
3876                 
3877             }
3878             
3879         });
3880         return prev;
3881     },
3882     /**
3883     * adds a Navigation item
3884     * @param {Roo.bootstrap.NavItem} the navitem to add
3885     */
3886     addItem : function(cfg)
3887     {
3888         var cn = new Roo.bootstrap.NavItem(cfg);
3889         this.register(cn);
3890         cn.parentId = this.id;
3891         cn.onRender(this.el, null);
3892         return cn;
3893     },
3894     /**
3895     * register a Navigation item
3896     * @param {Roo.bootstrap.NavItem} the navitem to add
3897     */
3898     register : function(item)
3899     {
3900         this.navItems.push( item);
3901         item.navId = this.navId;
3902     
3903     },
3904     
3905     /**
3906     * clear all the Navigation item
3907     */
3908    
3909     clearAll : function()
3910     {
3911         this.navItems = [];
3912         this.el.dom.innerHTML = '';
3913     },
3914     
3915     getNavItem: function(tabId)
3916     {
3917         var ret = false;
3918         Roo.each(this.navItems, function(e) {
3919             if (e.tabId == tabId) {
3920                ret =  e;
3921                return false;
3922             }
3923             return true;
3924             
3925         });
3926         return ret;
3927     },
3928     
3929     setActiveNext : function()
3930     {
3931         var i = this.indexOfNav(this.getActive());
3932         if (i > this.navItems.length) {
3933             return;
3934         }
3935         this.setActiveItem(this.navItems[i+1]);
3936     },
3937     setActivePrev : function()
3938     {
3939         var i = this.indexOfNav(this.getActive());
3940         if (i  < 1) {
3941             return;
3942         }
3943         this.setActiveItem(this.navItems[i-1]);
3944     },
3945     clearWasActive : function(except) {
3946         Roo.each(this.navItems, function(e) {
3947             if (e.tabId != except.tabId && e.was_active) {
3948                e.was_active = false;
3949                return false;
3950             }
3951             return true;
3952             
3953         });
3954     },
3955     getWasActive : function ()
3956     {
3957         var r = false;
3958         Roo.each(this.navItems, function(e) {
3959             if (e.was_active) {
3960                r = e;
3961                return false;
3962             }
3963             return true;
3964             
3965         });
3966         return r;
3967     }
3968     
3969     
3970 });
3971
3972  
3973 Roo.apply(Roo.bootstrap.NavGroup, {
3974     
3975     groups: {},
3976      /**
3977     * register a Navigation Group
3978     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3979     */
3980     register : function(navgrp)
3981     {
3982         this.groups[navgrp.navId] = navgrp;
3983         
3984     },
3985     /**
3986     * fetch a Navigation Group based on the navigation ID
3987     * @param {string} the navgroup to add
3988     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3989     */
3990     get: function(navId) {
3991         if (typeof(this.groups[navId]) == 'undefined') {
3992             return false;
3993             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3994         }
3995         return this.groups[navId] ;
3996     }
3997     
3998     
3999     
4000 });
4001
4002  /*
4003  * - LGPL
4004  *
4005  * row
4006  * 
4007  */
4008
4009 /**
4010  * @class Roo.bootstrap.NavItem
4011  * @extends Roo.bootstrap.Component
4012  * Bootstrap Navbar.NavItem class
4013  * @cfg {String} href  link to
4014  * @cfg {String} html content of button
4015  * @cfg {String} badge text inside badge
4016  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4017  * @cfg {String} glyphicon name of glyphicon
4018  * @cfg {String} icon name of font awesome icon
4019  * @cfg {Boolean} active Is item active
4020  * @cfg {Boolean} disabled Is item disabled
4021  
4022  * @cfg {Boolean} preventDefault (true | false) default false
4023  * @cfg {String} tabId the tab that this item activates.
4024  * @cfg {String} tagtype (a|span) render as a href or span?
4025  * @cfg {Boolean} animateRef (true|false) link to element default false  
4026   
4027  * @constructor
4028  * Create a new Navbar Item
4029  * @param {Object} config The config object
4030  */
4031 Roo.bootstrap.NavItem = function(config){
4032     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4033     this.addEvents({
4034         // raw events
4035         /**
4036          * @event click
4037          * The raw click event for the entire grid.
4038          * @param {Roo.EventObject} e
4039          */
4040         "click" : true,
4041          /**
4042             * @event changed
4043             * Fires when the active item active state changes
4044             * @param {Roo.bootstrap.NavItem} this
4045             * @param {boolean} state the new state
4046              
4047          */
4048         'changed': true,
4049         /**
4050             * @event scrollto
4051             * Fires when scroll to element
4052             * @param {Roo.bootstrap.NavItem} this
4053             * @param {Object} options
4054             * @param {Roo.EventObject} e
4055              
4056          */
4057         'scrollto': true
4058     });
4059    
4060 };
4061
4062 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4063     
4064     href: false,
4065     html: '',
4066     badge: '',
4067     icon: false,
4068     glyphicon: false,
4069     active: false,
4070     preventDefault : false,
4071     tabId : false,
4072     tagtype : 'a',
4073     disabled : false,
4074     animateRef : false,
4075     was_active : false,
4076     
4077     getAutoCreate : function(){
4078          
4079         var cfg = {
4080             tag: 'li',
4081             cls: 'nav-item'
4082             
4083         };
4084         
4085         if (this.active) {
4086             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4087         }
4088         if (this.disabled) {
4089             cfg.cls += ' disabled';
4090         }
4091         
4092         if (this.href || this.html || this.glyphicon || this.icon) {
4093             cfg.cn = [
4094                 {
4095                     tag: this.tagtype,
4096                     href : this.href || "#",
4097                     html: this.html || ''
4098                 }
4099             ];
4100             
4101             if (this.icon) {
4102                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4103             }
4104
4105             if(this.glyphicon) {
4106                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4107             }
4108             
4109             if (this.menu) {
4110                 
4111                 cfg.cn[0].html += " <span class='caret'></span>";
4112              
4113             }
4114             
4115             if (this.badge !== '') {
4116                  
4117                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4118             }
4119         }
4120         
4121         
4122         
4123         return cfg;
4124     },
4125     initEvents: function() 
4126     {
4127         if (typeof (this.menu) != 'undefined') {
4128             this.menu.parentType = this.xtype;
4129             this.menu.triggerEl = this.el;
4130             this.menu = this.addxtype(Roo.apply({}, this.menu));
4131         }
4132         
4133         this.el.select('a',true).on('click', this.onClick, this);
4134         
4135         if(this.tagtype == 'span'){
4136             this.el.select('span',true).on('click', this.onClick, this);
4137         }
4138        
4139         // at this point parent should be available..
4140         this.parent().register(this);
4141     },
4142     
4143     onClick : function(e)
4144     {
4145         if(
4146                 this.preventDefault || 
4147                 this.href == '#' 
4148         ){
4149             
4150             e.preventDefault();
4151         }
4152         
4153         if (this.disabled) {
4154             return;
4155         }
4156         
4157         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4158         if (tg && tg.transition) {
4159             Roo.log("waiting for the transitionend");
4160             return;
4161         }
4162         
4163         
4164         
4165         //Roo.log("fire event clicked");
4166         if(this.fireEvent('click', this, e) === false){
4167             return;
4168         };
4169         
4170         if(this.tagtype == 'span'){
4171             return;
4172         }
4173         
4174         //Roo.log(this.href);
4175         var ael = this.el.select('a',true).first();
4176         //Roo.log(ael);
4177         
4178         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4179             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4180             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4181                 return; // ignore... - it's a 'hash' to another page.
4182             }
4183             
4184             e.preventDefault();
4185             this.scrollToElement(e);
4186         }
4187         
4188         
4189         var p =  this.parent();
4190    
4191         if (['tabs','pills'].indexOf(p.type)!==-1) {
4192             if (typeof(p.setActiveItem) !== 'undefined') {
4193                 p.setActiveItem(this);
4194             }
4195         }
4196         
4197         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4198         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4199             // remove the collapsed menu expand...
4200             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4201         }
4202     },
4203     
4204     isActive: function () {
4205         return this.active
4206     },
4207     setActive : function(state, fire, is_was_active)
4208     {
4209         if (this.active && !state && this.navId) {
4210             this.was_active = true;
4211             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4212             if (nv) {
4213                 nv.clearWasActive(this);
4214             }
4215             
4216         }
4217         this.active = state;
4218         
4219         if (!state ) {
4220             this.el.removeClass('active');
4221         } else if (!this.el.hasClass('active')) {
4222             this.el.addClass('active');
4223         }
4224         if (fire) {
4225             this.fireEvent('changed', this, state);
4226         }
4227         
4228         // show a panel if it's registered and related..
4229         
4230         if (!this.navId || !this.tabId || !state || is_was_active) {
4231             return;
4232         }
4233         
4234         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4235         if (!tg) {
4236             return;
4237         }
4238         var pan = tg.getPanelByName(this.tabId);
4239         if (!pan) {
4240             return;
4241         }
4242         // if we can not flip to new panel - go back to old nav highlight..
4243         if (false == tg.showPanel(pan)) {
4244             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4245             if (nv) {
4246                 var onav = nv.getWasActive();
4247                 if (onav) {
4248                     onav.setActive(true, false, true);
4249                 }
4250             }
4251             
4252         }
4253         
4254         
4255         
4256     },
4257      // this should not be here...
4258     setDisabled : function(state)
4259     {
4260         this.disabled = state;
4261         if (!state ) {
4262             this.el.removeClass('disabled');
4263         } else if (!this.el.hasClass('disabled')) {
4264             this.el.addClass('disabled');
4265         }
4266         
4267     },
4268     
4269     /**
4270      * Fetch the element to display the tooltip on.
4271      * @return {Roo.Element} defaults to this.el
4272      */
4273     tooltipEl : function()
4274     {
4275         return this.el.select('' + this.tagtype + '', true).first();
4276     },
4277     
4278     scrollToElement : function(e)
4279     {
4280         var c = document.body;
4281         
4282         /*
4283          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4284          */
4285         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4286             c = document.documentElement;
4287         }
4288         
4289         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4290         
4291         if(!target){
4292             return;
4293         }
4294
4295         var o = target.calcOffsetsTo(c);
4296         
4297         var options = {
4298             target : target,
4299             value : o[1]
4300         };
4301         
4302         this.fireEvent('scrollto', this, options, e);
4303         
4304         Roo.get(c).scrollTo('top', options.value, true);
4305         
4306         return;
4307     }
4308 });
4309  
4310
4311  /*
4312  * - LGPL
4313  *
4314  * sidebar item
4315  *
4316  *  li
4317  *    <span> icon </span>
4318  *    <span> text </span>
4319  *    <span>badge </span>
4320  */
4321
4322 /**
4323  * @class Roo.bootstrap.NavSidebarItem
4324  * @extends Roo.bootstrap.NavItem
4325  * Bootstrap Navbar.NavSidebarItem class
4326  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4327  * @constructor
4328  * Create a new Navbar Button
4329  * @param {Object} config The config object
4330  */
4331 Roo.bootstrap.NavSidebarItem = function(config){
4332     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4333     this.addEvents({
4334         // raw events
4335         /**
4336          * @event click
4337          * The raw click event for the entire grid.
4338          * @param {Roo.EventObject} e
4339          */
4340         "click" : true,
4341          /**
4342             * @event changed
4343             * Fires when the active item active state changes
4344             * @param {Roo.bootstrap.NavSidebarItem} this
4345             * @param {boolean} state the new state
4346              
4347          */
4348         'changed': true
4349     });
4350    
4351 };
4352
4353 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4354     
4355     badgeWeight : 'default',
4356     
4357     getAutoCreate : function(){
4358         
4359         
4360         var a = {
4361                 tag: 'a',
4362                 href : this.href || '#',
4363                 cls: '',
4364                 html : '',
4365                 cn : []
4366         };
4367         var cfg = {
4368             tag: 'li',
4369             cls: '',
4370             cn: [ a ]
4371         };
4372         var span = {
4373             tag: 'span',
4374             html : this.html || ''
4375         };
4376         
4377         
4378         if (this.active) {
4379             cfg.cls += ' active';
4380         }
4381         
4382         if (this.disabled) {
4383             cfg.cls += ' disabled';
4384         }
4385         
4386         // left icon..
4387         if (this.glyphicon || this.icon) {
4388             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4389             a.cn.push({ tag : 'i', cls : c }) ;
4390         }
4391         // html..
4392         a.cn.push(span);
4393         // then badge..
4394         if (this.badge !== '') {
4395             
4396             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4397         }
4398         // fi
4399         if (this.menu) {
4400             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4401             a.cls += 'dropdown-toggle treeview' ;
4402             
4403         }
4404         
4405         
4406         
4407         return cfg;
4408          
4409            
4410     },
4411     
4412     initEvents : function()
4413     { 
4414         this.el.on('click', this.onClick, this);
4415        
4416     
4417         if(this.badge !== ''){
4418  
4419             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4420         }
4421         
4422     },
4423     
4424     onClick : function(e)
4425     {
4426         if(this.disabled){
4427             e.preventDefault();
4428             return;
4429         }
4430         
4431         if(this.preventDefault){
4432             e.preventDefault();
4433         }
4434         
4435         this.fireEvent('click', this);
4436     },
4437     
4438     disable : function()
4439     {
4440         this.setDisabled(true);
4441     },
4442     
4443     enable : function()
4444     {
4445         this.setDisabled(false);
4446     },
4447     
4448     setDisabled : function(state)
4449     {
4450         if(this.disabled == state){
4451             return;
4452         }
4453         
4454         this.disabled = state;
4455         
4456         if (state) {
4457             this.el.addClass('disabled');
4458             return;
4459         }
4460         
4461         this.el.removeClass('disabled');
4462         
4463         return;
4464     },
4465     
4466     setActive : function(state)
4467     {
4468         if(this.active == state){
4469             return;
4470         }
4471         
4472         this.active = state;
4473         
4474         if (state) {
4475             this.el.addClass('active');
4476             return;
4477         }
4478         
4479         this.el.removeClass('active');
4480         
4481         return;
4482     },
4483     
4484     isActive: function () 
4485     {
4486         return this.active;
4487     },
4488     
4489     setBadge : function(str)
4490     {
4491         if(!this.badgeEl){
4492             return;
4493         }
4494         
4495         this.badgeEl.dom.innerHTML = str;
4496     }
4497     
4498    
4499      
4500  
4501 });
4502  
4503
4504  /*
4505  * - LGPL
4506  *
4507  * row
4508  * 
4509  */
4510
4511 /**
4512  * @class Roo.bootstrap.Row
4513  * @extends Roo.bootstrap.Component
4514  * Bootstrap Row class (contains columns...)
4515  * 
4516  * @constructor
4517  * Create a new Row
4518  * @param {Object} config The config object
4519  */
4520
4521 Roo.bootstrap.Row = function(config){
4522     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4523 };
4524
4525 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4526     
4527     getAutoCreate : function(){
4528        return {
4529             cls: 'row clearfix'
4530        };
4531     }
4532     
4533     
4534 });
4535
4536  
4537
4538  /*
4539  * - LGPL
4540  *
4541  * element
4542  * 
4543  */
4544
4545 /**
4546  * @class Roo.bootstrap.Element
4547  * @extends Roo.bootstrap.Component
4548  * Bootstrap Element class
4549  * @cfg {String} html contents of the element
4550  * @cfg {String} tag tag of the element
4551  * @cfg {String} cls class of the element
4552  * @cfg {Boolean} preventDefault (true|false) default false
4553  * @cfg {Boolean} clickable (true|false) default false
4554  * 
4555  * @constructor
4556  * Create a new Element
4557  * @param {Object} config The config object
4558  */
4559
4560 Roo.bootstrap.Element = function(config){
4561     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4562     
4563     this.addEvents({
4564         // raw events
4565         /**
4566          * @event click
4567          * When a element is chick
4568          * @param {Roo.bootstrap.Element} this
4569          * @param {Roo.EventObject} e
4570          */
4571         "click" : true
4572     });
4573 };
4574
4575 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4576     
4577     tag: 'div',
4578     cls: '',
4579     html: '',
4580     preventDefault: false, 
4581     clickable: false,
4582     
4583     getAutoCreate : function(){
4584         
4585         var cfg = {
4586             tag: this.tag,
4587             cls: this.cls,
4588             html: this.html
4589         };
4590         
4591         return cfg;
4592     },
4593     
4594     initEvents: function() 
4595     {
4596         Roo.bootstrap.Element.superclass.initEvents.call(this);
4597         
4598         if(this.clickable){
4599             this.el.on('click', this.onClick, this);
4600         }
4601         
4602     },
4603     
4604     onClick : function(e)
4605     {
4606         if(this.preventDefault){
4607             e.preventDefault();
4608         }
4609         
4610         this.fireEvent('click', this, e);
4611     },
4612     
4613     getValue : function()
4614     {
4615         return this.el.dom.innerHTML;
4616     },
4617     
4618     setValue : function(value)
4619     {
4620         this.el.dom.innerHTML = value;
4621     }
4622    
4623 });
4624
4625  
4626
4627  /*
4628  * - LGPL
4629  *
4630  * pagination
4631  * 
4632  */
4633
4634 /**
4635  * @class Roo.bootstrap.Pagination
4636  * @extends Roo.bootstrap.Component
4637  * Bootstrap Pagination class
4638  * @cfg {String} size xs | sm | md | lg
4639  * @cfg {Boolean} inverse false | true
4640  * 
4641  * @constructor
4642  * Create a new Pagination
4643  * @param {Object} config The config object
4644  */
4645
4646 Roo.bootstrap.Pagination = function(config){
4647     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4648 };
4649
4650 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4651     
4652     cls: false,
4653     size: false,
4654     inverse: false,
4655     
4656     getAutoCreate : function(){
4657         var cfg = {
4658             tag: 'ul',
4659                 cls: 'pagination'
4660         };
4661         if (this.inverse) {
4662             cfg.cls += ' inverse';
4663         }
4664         if (this.html) {
4665             cfg.html=this.html;
4666         }
4667         if (this.cls) {
4668             cfg.cls += " " + this.cls;
4669         }
4670         return cfg;
4671     }
4672    
4673 });
4674
4675  
4676
4677  /*
4678  * - LGPL
4679  *
4680  * Pagination item
4681  * 
4682  */
4683
4684
4685 /**
4686  * @class Roo.bootstrap.PaginationItem
4687  * @extends Roo.bootstrap.Component
4688  * Bootstrap PaginationItem class
4689  * @cfg {String} html text
4690  * @cfg {String} href the link
4691  * @cfg {Boolean} preventDefault (true | false) default true
4692  * @cfg {Boolean} active (true | false) default false
4693  * @cfg {Boolean} disabled default false
4694  * 
4695  * 
4696  * @constructor
4697  * Create a new PaginationItem
4698  * @param {Object} config The config object
4699  */
4700
4701
4702 Roo.bootstrap.PaginationItem = function(config){
4703     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4704     this.addEvents({
4705         // raw events
4706         /**
4707          * @event click
4708          * The raw click event for the entire grid.
4709          * @param {Roo.EventObject} e
4710          */
4711         "click" : true
4712     });
4713 };
4714
4715 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4716     
4717     href : false,
4718     html : false,
4719     preventDefault: true,
4720     active : false,
4721     cls : false,
4722     disabled: false,
4723     
4724     getAutoCreate : function(){
4725         var cfg= {
4726             tag: 'li',
4727             cn: [
4728                 {
4729                     tag : 'a',
4730                     href : this.href ? this.href : '#',
4731                     html : this.html ? this.html : ''
4732                 }
4733             ]
4734         };
4735         
4736         if(this.cls){
4737             cfg.cls = this.cls;
4738         }
4739         
4740         if(this.disabled){
4741             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4742         }
4743         
4744         if(this.active){
4745             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4746         }
4747         
4748         return cfg;
4749     },
4750     
4751     initEvents: function() {
4752         
4753         this.el.on('click', this.onClick, this);
4754         
4755     },
4756     onClick : function(e)
4757     {
4758         Roo.log('PaginationItem on click ');
4759         if(this.preventDefault){
4760             e.preventDefault();
4761         }
4762         
4763         if(this.disabled){
4764             return;
4765         }
4766         
4767         this.fireEvent('click', this, e);
4768     }
4769    
4770 });
4771
4772  
4773
4774  /*
4775  * - LGPL
4776  *
4777  * slider
4778  * 
4779  */
4780
4781
4782 /**
4783  * @class Roo.bootstrap.Slider
4784  * @extends Roo.bootstrap.Component
4785  * Bootstrap Slider class
4786  *    
4787  * @constructor
4788  * Create a new Slider
4789  * @param {Object} config The config object
4790  */
4791
4792 Roo.bootstrap.Slider = function(config){
4793     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4794 };
4795
4796 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4797     
4798     getAutoCreate : function(){
4799         
4800         var cfg = {
4801             tag: 'div',
4802             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4803             cn: [
4804                 {
4805                     tag: 'a',
4806                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4807                 }
4808             ]
4809         };
4810         
4811         return cfg;
4812     }
4813    
4814 });
4815
4816  /*
4817  * Based on:
4818  * Ext JS Library 1.1.1
4819  * Copyright(c) 2006-2007, Ext JS, LLC.
4820  *
4821  * Originally Released Under LGPL - original licence link has changed is not relivant.
4822  *
4823  * Fork - LGPL
4824  * <script type="text/javascript">
4825  */
4826  
4827
4828 /**
4829  * @class Roo.grid.ColumnModel
4830  * @extends Roo.util.Observable
4831  * This is the default implementation of a ColumnModel used by the Grid. It defines
4832  * the columns in the grid.
4833  * <br>Usage:<br>
4834  <pre><code>
4835  var colModel = new Roo.grid.ColumnModel([
4836         {header: "Ticker", width: 60, sortable: true, locked: true},
4837         {header: "Company Name", width: 150, sortable: true},
4838         {header: "Market Cap.", width: 100, sortable: true},
4839         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4840         {header: "Employees", width: 100, sortable: true, resizable: false}
4841  ]);
4842  </code></pre>
4843  * <p>
4844  
4845  * The config options listed for this class are options which may appear in each
4846  * individual column definition.
4847  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4848  * @constructor
4849  * @param {Object} config An Array of column config objects. See this class's
4850  * config objects for details.
4851 */
4852 Roo.grid.ColumnModel = function(config){
4853         /**
4854      * The config passed into the constructor
4855      */
4856     this.config = config;
4857     this.lookup = {};
4858
4859     // if no id, create one
4860     // if the column does not have a dataIndex mapping,
4861     // map it to the order it is in the config
4862     for(var i = 0, len = config.length; i < len; i++){
4863         var c = config[i];
4864         if(typeof c.dataIndex == "undefined"){
4865             c.dataIndex = i;
4866         }
4867         if(typeof c.renderer == "string"){
4868             c.renderer = Roo.util.Format[c.renderer];
4869         }
4870         if(typeof c.id == "undefined"){
4871             c.id = Roo.id();
4872         }
4873         if(c.editor && c.editor.xtype){
4874             c.editor  = Roo.factory(c.editor, Roo.grid);
4875         }
4876         if(c.editor && c.editor.isFormField){
4877             c.editor = new Roo.grid.GridEditor(c.editor);
4878         }
4879         this.lookup[c.id] = c;
4880     }
4881
4882     /**
4883      * The width of columns which have no width specified (defaults to 100)
4884      * @type Number
4885      */
4886     this.defaultWidth = 100;
4887
4888     /**
4889      * Default sortable of columns which have no sortable specified (defaults to false)
4890      * @type Boolean
4891      */
4892     this.defaultSortable = false;
4893
4894     this.addEvents({
4895         /**
4896              * @event widthchange
4897              * Fires when the width of a column changes.
4898              * @param {ColumnModel} this
4899              * @param {Number} columnIndex The column index
4900              * @param {Number} newWidth The new width
4901              */
4902             "widthchange": true,
4903         /**
4904              * @event headerchange
4905              * Fires when the text of a header changes.
4906              * @param {ColumnModel} this
4907              * @param {Number} columnIndex The column index
4908              * @param {Number} newText The new header text
4909              */
4910             "headerchange": true,
4911         /**
4912              * @event hiddenchange
4913              * Fires when a column is hidden or "unhidden".
4914              * @param {ColumnModel} this
4915              * @param {Number} columnIndex The column index
4916              * @param {Boolean} hidden true if hidden, false otherwise
4917              */
4918             "hiddenchange": true,
4919             /**
4920          * @event columnmoved
4921          * Fires when a column is moved.
4922          * @param {ColumnModel} this
4923          * @param {Number} oldIndex
4924          * @param {Number} newIndex
4925          */
4926         "columnmoved" : true,
4927         /**
4928          * @event columlockchange
4929          * Fires when a column's locked state is changed
4930          * @param {ColumnModel} this
4931          * @param {Number} colIndex
4932          * @param {Boolean} locked true if locked
4933          */
4934         "columnlockchange" : true
4935     });
4936     Roo.grid.ColumnModel.superclass.constructor.call(this);
4937 };
4938 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4939     /**
4940      * @cfg {String} header The header text to display in the Grid view.
4941      */
4942     /**
4943      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4944      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4945      * specified, the column's index is used as an index into the Record's data Array.
4946      */
4947     /**
4948      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4949      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4950      */
4951     /**
4952      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4953      * Defaults to the value of the {@link #defaultSortable} property.
4954      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4955      */
4956     /**
4957      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4958      */
4959     /**
4960      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4961      */
4962     /**
4963      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4964      */
4965     /**
4966      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4967      */
4968     /**
4969      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4970      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4971      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4972      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4973      */
4974        /**
4975      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4976      */
4977     /**
4978      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4979      */
4980     /**
4981      * @cfg {String} cursor (Optional)
4982      */
4983     /**
4984      * @cfg {String} tooltip (Optional)
4985      */
4986     /**
4987      * @cfg {Number} xs (Optional)
4988      */
4989     /**
4990      * @cfg {Number} sm (Optional)
4991      */
4992     /**
4993      * @cfg {Number} md (Optional)
4994      */
4995     /**
4996      * @cfg {Number} lg (Optional)
4997      */
4998     /**
4999      * Returns the id of the column at the specified index.
5000      * @param {Number} index The column index
5001      * @return {String} the id
5002      */
5003     getColumnId : function(index){
5004         return this.config[index].id;
5005     },
5006
5007     /**
5008      * Returns the column for a specified id.
5009      * @param {String} id The column id
5010      * @return {Object} the column
5011      */
5012     getColumnById : function(id){
5013         return this.lookup[id];
5014     },
5015
5016     
5017     /**
5018      * Returns the column for a specified dataIndex.
5019      * @param {String} dataIndex The column dataIndex
5020      * @return {Object|Boolean} the column or false if not found
5021      */
5022     getColumnByDataIndex: function(dataIndex){
5023         var index = this.findColumnIndex(dataIndex);
5024         return index > -1 ? this.config[index] : false;
5025     },
5026     
5027     /**
5028      * Returns the index for a specified column id.
5029      * @param {String} id The column id
5030      * @return {Number} the index, or -1 if not found
5031      */
5032     getIndexById : function(id){
5033         for(var i = 0, len = this.config.length; i < len; i++){
5034             if(this.config[i].id == id){
5035                 return i;
5036             }
5037         }
5038         return -1;
5039     },
5040     
5041     /**
5042      * Returns the index for a specified column dataIndex.
5043      * @param {String} dataIndex The column dataIndex
5044      * @return {Number} the index, or -1 if not found
5045      */
5046     
5047     findColumnIndex : function(dataIndex){
5048         for(var i = 0, len = this.config.length; i < len; i++){
5049             if(this.config[i].dataIndex == dataIndex){
5050                 return i;
5051             }
5052         }
5053         return -1;
5054     },
5055     
5056     
5057     moveColumn : function(oldIndex, newIndex){
5058         var c = this.config[oldIndex];
5059         this.config.splice(oldIndex, 1);
5060         this.config.splice(newIndex, 0, c);
5061         this.dataMap = null;
5062         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5063     },
5064
5065     isLocked : function(colIndex){
5066         return this.config[colIndex].locked === true;
5067     },
5068
5069     setLocked : function(colIndex, value, suppressEvent){
5070         if(this.isLocked(colIndex) == value){
5071             return;
5072         }
5073         this.config[colIndex].locked = value;
5074         if(!suppressEvent){
5075             this.fireEvent("columnlockchange", this, colIndex, value);
5076         }
5077     },
5078
5079     getTotalLockedWidth : function(){
5080         var totalWidth = 0;
5081         for(var i = 0; i < this.config.length; i++){
5082             if(this.isLocked(i) && !this.isHidden(i)){
5083                 this.totalWidth += this.getColumnWidth(i);
5084             }
5085         }
5086         return totalWidth;
5087     },
5088
5089     getLockedCount : function(){
5090         for(var i = 0, len = this.config.length; i < len; i++){
5091             if(!this.isLocked(i)){
5092                 return i;
5093             }
5094         }
5095     },
5096
5097     /**
5098      * Returns the number of columns.
5099      * @return {Number}
5100      */
5101     getColumnCount : function(visibleOnly){
5102         if(visibleOnly === true){
5103             var c = 0;
5104             for(var i = 0, len = this.config.length; i < len; i++){
5105                 if(!this.isHidden(i)){
5106                     c++;
5107                 }
5108             }
5109             return c;
5110         }
5111         return this.config.length;
5112     },
5113
5114     /**
5115      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5116      * @param {Function} fn
5117      * @param {Object} scope (optional)
5118      * @return {Array} result
5119      */
5120     getColumnsBy : function(fn, scope){
5121         var r = [];
5122         for(var i = 0, len = this.config.length; i < len; i++){
5123             var c = this.config[i];
5124             if(fn.call(scope||this, c, i) === true){
5125                 r[r.length] = c;
5126             }
5127         }
5128         return r;
5129     },
5130
5131     /**
5132      * Returns true if the specified column is sortable.
5133      * @param {Number} col The column index
5134      * @return {Boolean}
5135      */
5136     isSortable : function(col){
5137         if(typeof this.config[col].sortable == "undefined"){
5138             return this.defaultSortable;
5139         }
5140         return this.config[col].sortable;
5141     },
5142
5143     /**
5144      * Returns the rendering (formatting) function defined for the column.
5145      * @param {Number} col The column index.
5146      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5147      */
5148     getRenderer : function(col){
5149         if(!this.config[col].renderer){
5150             return Roo.grid.ColumnModel.defaultRenderer;
5151         }
5152         return this.config[col].renderer;
5153     },
5154
5155     /**
5156      * Sets the rendering (formatting) function for a column.
5157      * @param {Number} col The column index
5158      * @param {Function} fn The function to use to process the cell's raw data
5159      * to return HTML markup for the grid view. The render function is called with
5160      * the following parameters:<ul>
5161      * <li>Data value.</li>
5162      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5163      * <li>css A CSS style string to apply to the table cell.</li>
5164      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5165      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5166      * <li>Row index</li>
5167      * <li>Column index</li>
5168      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5169      */
5170     setRenderer : function(col, fn){
5171         this.config[col].renderer = fn;
5172     },
5173
5174     /**
5175      * Returns the width for the specified column.
5176      * @param {Number} col The column index
5177      * @return {Number}
5178      */
5179     getColumnWidth : function(col){
5180         return this.config[col].width * 1 || this.defaultWidth;
5181     },
5182
5183     /**
5184      * Sets the width for a column.
5185      * @param {Number} col The column index
5186      * @param {Number} width The new width
5187      */
5188     setColumnWidth : function(col, width, suppressEvent){
5189         this.config[col].width = width;
5190         this.totalWidth = null;
5191         if(!suppressEvent){
5192              this.fireEvent("widthchange", this, col, width);
5193         }
5194     },
5195
5196     /**
5197      * Returns the total width of all columns.
5198      * @param {Boolean} includeHidden True to include hidden column widths
5199      * @return {Number}
5200      */
5201     getTotalWidth : function(includeHidden){
5202         if(!this.totalWidth){
5203             this.totalWidth = 0;
5204             for(var i = 0, len = this.config.length; i < len; i++){
5205                 if(includeHidden || !this.isHidden(i)){
5206                     this.totalWidth += this.getColumnWidth(i);
5207                 }
5208             }
5209         }
5210         return this.totalWidth;
5211     },
5212
5213     /**
5214      * Returns the header for the specified column.
5215      * @param {Number} col The column index
5216      * @return {String}
5217      */
5218     getColumnHeader : function(col){
5219         return this.config[col].header;
5220     },
5221
5222     /**
5223      * Sets the header for a column.
5224      * @param {Number} col The column index
5225      * @param {String} header The new header
5226      */
5227     setColumnHeader : function(col, header){
5228         this.config[col].header = header;
5229         this.fireEvent("headerchange", this, col, header);
5230     },
5231
5232     /**
5233      * Returns the tooltip for the specified column.
5234      * @param {Number} col The column index
5235      * @return {String}
5236      */
5237     getColumnTooltip : function(col){
5238             return this.config[col].tooltip;
5239     },
5240     /**
5241      * Sets the tooltip for a column.
5242      * @param {Number} col The column index
5243      * @param {String} tooltip The new tooltip
5244      */
5245     setColumnTooltip : function(col, tooltip){
5246             this.config[col].tooltip = tooltip;
5247     },
5248
5249     /**
5250      * Returns the dataIndex for the specified column.
5251      * @param {Number} col The column index
5252      * @return {Number}
5253      */
5254     getDataIndex : function(col){
5255         return this.config[col].dataIndex;
5256     },
5257
5258     /**
5259      * Sets the dataIndex for a column.
5260      * @param {Number} col The column index
5261      * @param {Number} dataIndex The new dataIndex
5262      */
5263     setDataIndex : function(col, dataIndex){
5264         this.config[col].dataIndex = dataIndex;
5265     },
5266
5267     
5268     
5269     /**
5270      * Returns true if the cell is editable.
5271      * @param {Number} colIndex The column index
5272      * @param {Number} rowIndex The row index
5273      * @return {Boolean}
5274      */
5275     isCellEditable : function(colIndex, rowIndex){
5276         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5277     },
5278
5279     /**
5280      * Returns the editor defined for the cell/column.
5281      * return false or null to disable editing.
5282      * @param {Number} colIndex The column index
5283      * @param {Number} rowIndex The row index
5284      * @return {Object}
5285      */
5286     getCellEditor : function(colIndex, rowIndex){
5287         return this.config[colIndex].editor;
5288     },
5289
5290     /**
5291      * Sets if a column is editable.
5292      * @param {Number} col The column index
5293      * @param {Boolean} editable True if the column is editable
5294      */
5295     setEditable : function(col, editable){
5296         this.config[col].editable = editable;
5297     },
5298
5299
5300     /**
5301      * Returns true if the column is hidden.
5302      * @param {Number} colIndex The column index
5303      * @return {Boolean}
5304      */
5305     isHidden : function(colIndex){
5306         return this.config[colIndex].hidden;
5307     },
5308
5309
5310     /**
5311      * Returns true if the column width cannot be changed
5312      */
5313     isFixed : function(colIndex){
5314         return this.config[colIndex].fixed;
5315     },
5316
5317     /**
5318      * Returns true if the column can be resized
5319      * @return {Boolean}
5320      */
5321     isResizable : function(colIndex){
5322         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5323     },
5324     /**
5325      * Sets if a column is hidden.
5326      * @param {Number} colIndex The column index
5327      * @param {Boolean} hidden True if the column is hidden
5328      */
5329     setHidden : function(colIndex, hidden){
5330         this.config[colIndex].hidden = hidden;
5331         this.totalWidth = null;
5332         this.fireEvent("hiddenchange", this, colIndex, hidden);
5333     },
5334
5335     /**
5336      * Sets the editor for a column.
5337      * @param {Number} col The column index
5338      * @param {Object} editor The editor object
5339      */
5340     setEditor : function(col, editor){
5341         this.config[col].editor = editor;
5342     }
5343 });
5344
5345 Roo.grid.ColumnModel.defaultRenderer = function(value){
5346         if(typeof value == "string" && value.length < 1){
5347             return "&#160;";
5348         }
5349         return value;
5350 };
5351
5352 // Alias for backwards compatibility
5353 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5354 /*
5355  * Based on:
5356  * Ext JS Library 1.1.1
5357  * Copyright(c) 2006-2007, Ext JS, LLC.
5358  *
5359  * Originally Released Under LGPL - original licence link has changed is not relivant.
5360  *
5361  * Fork - LGPL
5362  * <script type="text/javascript">
5363  */
5364  
5365 /**
5366  * @class Roo.LoadMask
5367  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5368  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5369  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5370  * element's UpdateManager load indicator and will be destroyed after the initial load.
5371  * @constructor
5372  * Create a new LoadMask
5373  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5374  * @param {Object} config The config object
5375  */
5376 Roo.LoadMask = function(el, config){
5377     this.el = Roo.get(el);
5378     Roo.apply(this, config);
5379     if(this.store){
5380         this.store.on('beforeload', this.onBeforeLoad, this);
5381         this.store.on('load', this.onLoad, this);
5382         this.store.on('loadexception', this.onLoadException, this);
5383         this.removeMask = false;
5384     }else{
5385         var um = this.el.getUpdateManager();
5386         um.showLoadIndicator = false; // disable the default indicator
5387         um.on('beforeupdate', this.onBeforeLoad, this);
5388         um.on('update', this.onLoad, this);
5389         um.on('failure', this.onLoad, this);
5390         this.removeMask = true;
5391     }
5392 };
5393
5394 Roo.LoadMask.prototype = {
5395     /**
5396      * @cfg {Boolean} removeMask
5397      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5398      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5399      */
5400     /**
5401      * @cfg {String} msg
5402      * The text to display in a centered loading message box (defaults to 'Loading...')
5403      */
5404     msg : 'Loading...',
5405     /**
5406      * @cfg {String} msgCls
5407      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5408      */
5409     msgCls : 'x-mask-loading',
5410
5411     /**
5412      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5413      * @type Boolean
5414      */
5415     disabled: false,
5416
5417     /**
5418      * Disables the mask to prevent it from being displayed
5419      */
5420     disable : function(){
5421        this.disabled = true;
5422     },
5423
5424     /**
5425      * Enables the mask so that it can be displayed
5426      */
5427     enable : function(){
5428         this.disabled = false;
5429     },
5430     
5431     onLoadException : function()
5432     {
5433         Roo.log(arguments);
5434         
5435         if (typeof(arguments[3]) != 'undefined') {
5436             Roo.MessageBox.alert("Error loading",arguments[3]);
5437         } 
5438         /*
5439         try {
5440             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5441                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5442             }   
5443         } catch(e) {
5444             
5445         }
5446         */
5447     
5448         
5449         
5450         this.el.unmask(this.removeMask);
5451     },
5452     // private
5453     onLoad : function()
5454     {
5455         this.el.unmask(this.removeMask);
5456     },
5457
5458     // private
5459     onBeforeLoad : function(){
5460         if(!this.disabled){
5461             this.el.mask(this.msg, this.msgCls);
5462         }
5463     },
5464
5465     // private
5466     destroy : function(){
5467         if(this.store){
5468             this.store.un('beforeload', this.onBeforeLoad, this);
5469             this.store.un('load', this.onLoad, this);
5470             this.store.un('loadexception', this.onLoadException, this);
5471         }else{
5472             var um = this.el.getUpdateManager();
5473             um.un('beforeupdate', this.onBeforeLoad, this);
5474             um.un('update', this.onLoad, this);
5475             um.un('failure', this.onLoad, this);
5476         }
5477     }
5478 };/*
5479  * - LGPL
5480  *
5481  * table
5482  * 
5483  */
5484
5485 /**
5486  * @class Roo.bootstrap.Table
5487  * @extends Roo.bootstrap.Component
5488  * Bootstrap Table class
5489  * @cfg {String} cls table class
5490  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5491  * @cfg {String} bgcolor Specifies the background color for a table
5492  * @cfg {Number} border Specifies whether the table cells should have borders or not
5493  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5494  * @cfg {Number} cellspacing Specifies the space between cells
5495  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5496  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5497  * @cfg {String} sortable Specifies that the table should be sortable
5498  * @cfg {String} summary Specifies a summary of the content of a table
5499  * @cfg {Number} width Specifies the width of a table
5500  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5501  * 
5502  * @cfg {boolean} striped Should the rows be alternative striped
5503  * @cfg {boolean} bordered Add borders to the table
5504  * @cfg {boolean} hover Add hover highlighting
5505  * @cfg {boolean} condensed Format condensed
5506  * @cfg {boolean} responsive Format condensed
5507  * @cfg {Boolean} loadMask (true|false) default false
5508  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5509  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5510  * @cfg {Boolean} rowSelection (true|false) default false
5511  * @cfg {Boolean} cellSelection (true|false) default false
5512  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5513  
5514  * 
5515  * @constructor
5516  * Create a new Table
5517  * @param {Object} config The config object
5518  */
5519
5520 Roo.bootstrap.Table = function(config){
5521     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5522     
5523     // BC...
5524     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5525     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5526     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5527     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5528     
5529     
5530     if (this.sm) {
5531         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5532         this.sm = this.selModel;
5533         this.sm.xmodule = this.xmodule || false;
5534     }
5535     if (this.cm && typeof(this.cm.config) == 'undefined') {
5536         this.colModel = new Roo.grid.ColumnModel(this.cm);
5537         this.cm = this.colModel;
5538         this.cm.xmodule = this.xmodule || false;
5539     }
5540     if (this.store) {
5541         this.store= Roo.factory(this.store, Roo.data);
5542         this.ds = this.store;
5543         this.ds.xmodule = this.xmodule || false;
5544          
5545     }
5546     if (this.footer && this.store) {
5547         this.footer.dataSource = this.ds;
5548         this.footer = Roo.factory(this.footer);
5549     }
5550     
5551     /** @private */
5552     this.addEvents({
5553         /**
5554          * @event cellclick
5555          * Fires when a cell is clicked
5556          * @param {Roo.bootstrap.Table} this
5557          * @param {Roo.Element} el
5558          * @param {Number} rowIndex
5559          * @param {Number} columnIndex
5560          * @param {Roo.EventObject} e
5561          */
5562         "cellclick" : true,
5563         /**
5564          * @event celldblclick
5565          * Fires when a cell is double clicked
5566          * @param {Roo.bootstrap.Table} this
5567          * @param {Roo.Element} el
5568          * @param {Number} rowIndex
5569          * @param {Number} columnIndex
5570          * @param {Roo.EventObject} e
5571          */
5572         "celldblclick" : true,
5573         /**
5574          * @event rowclick
5575          * Fires when a row is clicked
5576          * @param {Roo.bootstrap.Table} this
5577          * @param {Roo.Element} el
5578          * @param {Number} rowIndex
5579          * @param {Roo.EventObject} e
5580          */
5581         "rowclick" : true,
5582         /**
5583          * @event rowdblclick
5584          * Fires when a row is double clicked
5585          * @param {Roo.bootstrap.Table} this
5586          * @param {Roo.Element} el
5587          * @param {Number} rowIndex
5588          * @param {Roo.EventObject} e
5589          */
5590         "rowdblclick" : true,
5591         /**
5592          * @event mouseover
5593          * Fires when a mouseover occur
5594          * @param {Roo.bootstrap.Table} this
5595          * @param {Roo.Element} el
5596          * @param {Number} rowIndex
5597          * @param {Number} columnIndex
5598          * @param {Roo.EventObject} e
5599          */
5600         "mouseover" : true,
5601         /**
5602          * @event mouseout
5603          * Fires when a mouseout occur
5604          * @param {Roo.bootstrap.Table} this
5605          * @param {Roo.Element} el
5606          * @param {Number} rowIndex
5607          * @param {Number} columnIndex
5608          * @param {Roo.EventObject} e
5609          */
5610         "mouseout" : true,
5611         /**
5612          * @event rowclass
5613          * Fires when a row is rendered, so you can change add a style to it.
5614          * @param {Roo.bootstrap.Table} this
5615          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5616          */
5617         'rowclass' : true,
5618           /**
5619          * @event rowsrendered
5620          * Fires when all the  rows have been rendered
5621          * @param {Roo.bootstrap.Table} this
5622          */
5623         'rowsrendered' : true
5624         
5625     });
5626 };
5627
5628 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5629     
5630     cls: false,
5631     align: false,
5632     bgcolor: false,
5633     border: false,
5634     cellpadding: false,
5635     cellspacing: false,
5636     frame: false,
5637     rules: false,
5638     sortable: false,
5639     summary: false,
5640     width: false,
5641     striped : false,
5642     bordered: false,
5643     hover:  false,
5644     condensed : false,
5645     responsive : false,
5646     sm : false,
5647     cm : false,
5648     store : false,
5649     loadMask : false,
5650     footerShow : true,
5651     headerShow : true,
5652   
5653     rowSelection : false,
5654     cellSelection : false,
5655     layout : false,
5656     
5657     // Roo.Element - the tbody
5658     mainBody: false, 
5659     
5660     getAutoCreate : function(){
5661         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5662         
5663         cfg = {
5664             tag: 'table',
5665             cls : 'table',
5666             cn : []
5667         };
5668             
5669         if (this.striped) {
5670             cfg.cls += ' table-striped';
5671         }
5672         
5673         if (this.hover) {
5674             cfg.cls += ' table-hover';
5675         }
5676         if (this.bordered) {
5677             cfg.cls += ' table-bordered';
5678         }
5679         if (this.condensed) {
5680             cfg.cls += ' table-condensed';
5681         }
5682         if (this.responsive) {
5683             cfg.cls += ' table-responsive';
5684         }
5685         
5686         if (this.cls) {
5687             cfg.cls+=  ' ' +this.cls;
5688         }
5689         
5690         // this lot should be simplifed...
5691         
5692         if (this.align) {
5693             cfg.align=this.align;
5694         }
5695         if (this.bgcolor) {
5696             cfg.bgcolor=this.bgcolor;
5697         }
5698         if (this.border) {
5699             cfg.border=this.border;
5700         }
5701         if (this.cellpadding) {
5702             cfg.cellpadding=this.cellpadding;
5703         }
5704         if (this.cellspacing) {
5705             cfg.cellspacing=this.cellspacing;
5706         }
5707         if (this.frame) {
5708             cfg.frame=this.frame;
5709         }
5710         if (this.rules) {
5711             cfg.rules=this.rules;
5712         }
5713         if (this.sortable) {
5714             cfg.sortable=this.sortable;
5715         }
5716         if (this.summary) {
5717             cfg.summary=this.summary;
5718         }
5719         if (this.width) {
5720             cfg.width=this.width;
5721         }
5722         if (this.layout) {
5723             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5724         }
5725         
5726         if(this.store || this.cm){
5727             if(this.headerShow){
5728                 cfg.cn.push(this.renderHeader());
5729             }
5730             
5731             cfg.cn.push(this.renderBody());
5732             
5733             if(this.footerShow){
5734                 cfg.cn.push(this.renderFooter());
5735             }
5736             
5737             cfg.cls+=  ' TableGrid';
5738         }
5739         
5740         return { cn : [ cfg ] };
5741     },
5742     
5743     initEvents : function()
5744     {   
5745         if(!this.store || !this.cm){
5746             return;
5747         }
5748         
5749         //Roo.log('initEvents with ds!!!!');
5750         
5751         this.mainBody = this.el.select('tbody', true).first();
5752         
5753         
5754         var _this = this;
5755         
5756         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5757             e.on('click', _this.sort, _this);
5758         });
5759         
5760         this.el.on("click", this.onClick, this);
5761         this.el.on("dblclick", this.onDblClick, this);
5762         
5763         // why is this done????? = it breaks dialogs??
5764         //this.parent().el.setStyle('position', 'relative');
5765         
5766         
5767         if (this.footer) {
5768             this.footer.parentId = this.id;
5769             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5770         }
5771         
5772         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5773         
5774         this.store.on('load', this.onLoad, this);
5775         this.store.on('beforeload', this.onBeforeLoad, this);
5776         this.store.on('update', this.onUpdate, this);
5777         this.store.on('add', this.onAdd, this);
5778         
5779     },
5780     
5781     onMouseover : function(e, el)
5782     {
5783         var cell = Roo.get(el);
5784         
5785         if(!cell){
5786             return;
5787         }
5788         
5789         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5790             cell = cell.findParent('td', false, true);
5791         }
5792         
5793         var row = cell.findParent('tr', false, true);
5794         var cellIndex = cell.dom.cellIndex;
5795         var rowIndex = row.dom.rowIndex - 1; // start from 0
5796         
5797         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5798         
5799     },
5800     
5801     onMouseout : function(e, el)
5802     {
5803         var cell = Roo.get(el);
5804         
5805         if(!cell){
5806             return;
5807         }
5808         
5809         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810             cell = cell.findParent('td', false, true);
5811         }
5812         
5813         var row = cell.findParent('tr', false, true);
5814         var cellIndex = cell.dom.cellIndex;
5815         var rowIndex = row.dom.rowIndex - 1; // start from 0
5816         
5817         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5818         
5819     },
5820     
5821     onClick : function(e, el)
5822     {
5823         var cell = Roo.get(el);
5824         
5825         if(!cell || (!this.cellSelection && !this.rowSelection)){
5826             return;
5827         }
5828         
5829         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830             cell = cell.findParent('td', false, true);
5831         }
5832         
5833         if(!cell || typeof(cell) == 'undefined'){
5834             return;
5835         }
5836         
5837         var row = cell.findParent('tr', false, true);
5838         
5839         if(!row || typeof(row) == 'undefined'){
5840             return;
5841         }
5842         
5843         var cellIndex = cell.dom.cellIndex;
5844         var rowIndex = this.getRowIndex(row);
5845         
5846         // why??? - should these not be based on SelectionModel?
5847         if(this.cellSelection){
5848             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5849         }
5850         
5851         if(this.rowSelection){
5852             this.fireEvent('rowclick', this, row, rowIndex, e);
5853         }
5854         
5855         
5856     },
5857     
5858     onDblClick : function(e,el)
5859     {
5860         var cell = Roo.get(el);
5861         
5862         if(!cell || (!this.CellSelection && !this.RowSelection)){
5863             return;
5864         }
5865         
5866         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5867             cell = cell.findParent('td', false, true);
5868         }
5869         
5870         if(!cell || typeof(cell) == 'undefined'){
5871             return;
5872         }
5873         
5874         var row = cell.findParent('tr', false, true);
5875         
5876         if(!row || typeof(row) == 'undefined'){
5877             return;
5878         }
5879         
5880         var cellIndex = cell.dom.cellIndex;
5881         var rowIndex = this.getRowIndex(row);
5882         
5883         if(this.CellSelection){
5884             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5885         }
5886         
5887         if(this.RowSelection){
5888             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5889         }
5890     },
5891     
5892     sort : function(e,el)
5893     {
5894         var col = Roo.get(el);
5895         
5896         if(!col.hasClass('sortable')){
5897             return;
5898         }
5899         
5900         var sort = col.attr('sort');
5901         var dir = 'ASC';
5902         
5903         if(col.hasClass('glyphicon-arrow-up')){
5904             dir = 'DESC';
5905         }
5906         
5907         this.store.sortInfo = {field : sort, direction : dir};
5908         
5909         if (this.footer) {
5910             Roo.log("calling footer first");
5911             this.footer.onClick('first');
5912         } else {
5913         
5914             this.store.load({ params : { start : 0 } });
5915         }
5916     },
5917     
5918     renderHeader : function()
5919     {
5920         var header = {
5921             tag: 'thead',
5922             cn : []
5923         };
5924         
5925         var cm = this.cm;
5926         
5927         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5928             
5929             var config = cm.config[i];
5930             
5931             var c = {
5932                 tag: 'th',
5933                 style : '',
5934                 html: cm.getColumnHeader(i)
5935             };
5936             
5937             var hh = '';
5938             
5939             if(typeof(config.lgHeader) != 'undefined'){
5940                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5941             }
5942             
5943             if(typeof(config.mdHeader) != 'undefined'){
5944                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5945             }
5946             
5947             if(typeof(config.smHeader) != 'undefined'){
5948                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5949             }
5950             
5951             if(typeof(config.xsHeader) != 'undefined'){
5952                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5953             }
5954             
5955             if(hh.length){
5956                 c.html = hh;
5957             }
5958             
5959             if(typeof(config.tooltip) != 'undefined'){
5960                 c.tooltip = config.tooltip;
5961             }
5962             
5963             if(typeof(config.colspan) != 'undefined'){
5964                 c.colspan = config.colspan;
5965             }
5966             
5967             if(typeof(config.hidden) != 'undefined' && config.hidden){
5968                 c.style += ' display:none;';
5969             }
5970             
5971             if(typeof(config.dataIndex) != 'undefined'){
5972                 c.sort = config.dataIndex;
5973             }
5974             
5975             if(typeof(config.sortable) != 'undefined' && config.sortable){
5976                 c.cls = 'sortable';
5977             }
5978             
5979             if(typeof(config.align) != 'undefined' && config.align.length){
5980                 c.style += ' text-align:' + config.align + ';';
5981             }
5982             
5983             if(typeof(config.width) != 'undefined'){
5984                 c.style += ' width:' + config.width + 'px;';
5985             }
5986             
5987             if(typeof(config.cls) != 'undefined'){
5988                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5989             }
5990             
5991             ['xs','sm','md','lg'].map(function(size){
5992                 
5993                 if(typeof(config[size]) == 'undefined'){
5994                     return;
5995                 }
5996                 
5997                 if (!config[size]) { // 0 = hidden
5998                     cfg.cls += ' hidden-' + size;
5999                     return;
6000                 }
6001                 
6002                 cfg.cls += ' col-' + size + '-' + config[size];
6003
6004             });
6005             
6006             header.cn.push(c)
6007         }
6008         
6009         return header;
6010     },
6011     
6012     renderBody : function()
6013     {
6014         var body = {
6015             tag: 'tbody',
6016             cn : [
6017                 {
6018                     tag: 'tr',
6019                     cn : [
6020                         {
6021                             tag : 'td',
6022                             colspan :  this.cm.getColumnCount()
6023                         }
6024                     ]
6025                 }
6026             ]
6027         };
6028         
6029         return body;
6030     },
6031     
6032     renderFooter : function()
6033     {
6034         var footer = {
6035             tag: 'tfoot',
6036             cn : [
6037                 {
6038                     tag: 'tr',
6039                     cn : [
6040                         {
6041                             tag : 'td',
6042                             colspan :  this.cm.getColumnCount()
6043                         }
6044                     ]
6045                 }
6046             ]
6047         };
6048         
6049         return footer;
6050     },
6051     
6052     
6053     
6054     onLoad : function()
6055     {
6056         Roo.log('ds onload');
6057         this.clear();
6058         
6059         var _this = this;
6060         var cm = this.cm;
6061         var ds = this.store;
6062         
6063         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6065             
6066             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6067                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6068             }
6069             
6070             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6071                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6072             }
6073         });
6074         
6075         var tbody =  this.mainBody;
6076               
6077         if(ds.getCount() > 0){
6078             ds.data.each(function(d,rowIndex){
6079                 var row =  this.renderRow(cm, ds, rowIndex);
6080                 
6081                 tbody.createChild(row);
6082                 
6083                 var _this = this;
6084                 
6085                 if(row.cellObjects.length){
6086                     Roo.each(row.cellObjects, function(r){
6087                         _this.renderCellObject(r);
6088                     })
6089                 }
6090                 
6091             }, this);
6092         }
6093         
6094         Roo.each(this.el.select('tbody td', true).elements, function(e){
6095             e.on('mouseover', _this.onMouseover, _this);
6096         });
6097         
6098         Roo.each(this.el.select('tbody td', true).elements, function(e){
6099             e.on('mouseout', _this.onMouseout, _this);
6100         });
6101         this.fireEvent('rowsrendered', this);
6102         //if(this.loadMask){
6103         //    this.maskEl.hide();
6104         //}
6105     },
6106     
6107     
6108     onUpdate : function(ds,record)
6109     {
6110         this.refreshRow(record);
6111     },
6112     
6113     onRemove : function(ds, record, index, isUpdate){
6114         if(isUpdate !== true){
6115             this.fireEvent("beforerowremoved", this, index, record);
6116         }
6117         var bt = this.mainBody.dom;
6118         
6119         var rows = this.el.select('tbody > tr', true).elements;
6120         
6121         if(typeof(rows[index]) != 'undefined'){
6122             bt.removeChild(rows[index].dom);
6123         }
6124         
6125 //        if(bt.rows[index]){
6126 //            bt.removeChild(bt.rows[index]);
6127 //        }
6128         
6129         if(isUpdate !== true){
6130             //this.stripeRows(index);
6131             //this.syncRowHeights(index, index);
6132             //this.layout();
6133             this.fireEvent("rowremoved", this, index, record);
6134         }
6135     },
6136     
6137     onAdd : function(ds, records, rowIndex)
6138     {
6139         //Roo.log('on Add called');
6140         // - note this does not handle multiple adding very well..
6141         var bt = this.mainBody.dom;
6142         for (var i =0 ; i < records.length;i++) {
6143             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6144             //Roo.log(records[i]);
6145             //Roo.log(this.store.getAt(rowIndex+i));
6146             this.insertRow(this.store, rowIndex + i, false);
6147             return;
6148         }
6149         
6150     },
6151     
6152     
6153     refreshRow : function(record){
6154         var ds = this.store, index;
6155         if(typeof record == 'number'){
6156             index = record;
6157             record = ds.getAt(index);
6158         }else{
6159             index = ds.indexOf(record);
6160         }
6161         this.insertRow(ds, index, true);
6162         this.onRemove(ds, record, index+1, true);
6163         //this.syncRowHeights(index, index);
6164         //this.layout();
6165         this.fireEvent("rowupdated", this, index, record);
6166     },
6167     
6168     insertRow : function(dm, rowIndex, isUpdate){
6169         
6170         if(!isUpdate){
6171             this.fireEvent("beforerowsinserted", this, rowIndex);
6172         }
6173             //var s = this.getScrollState();
6174         var row = this.renderRow(this.cm, this.store, rowIndex);
6175         // insert before rowIndex..
6176         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6177         
6178         var _this = this;
6179                 
6180         if(row.cellObjects.length){
6181             Roo.each(row.cellObjects, function(r){
6182                 _this.renderCellObject(r);
6183             })
6184         }
6185             
6186         if(!isUpdate){
6187             this.fireEvent("rowsinserted", this, rowIndex);
6188             //this.syncRowHeights(firstRow, lastRow);
6189             //this.stripeRows(firstRow);
6190             //this.layout();
6191         }
6192         
6193     },
6194     
6195     
6196     getRowDom : function(rowIndex)
6197     {
6198         var rows = this.el.select('tbody > tr', true).elements;
6199         
6200         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6201         
6202     },
6203     // returns the object tree for a tr..
6204   
6205     
6206     renderRow : function(cm, ds, rowIndex) 
6207     {
6208         
6209         var d = ds.getAt(rowIndex);
6210         
6211         var row = {
6212             tag : 'tr',
6213             cn : []
6214         };
6215             
6216         var cellObjects = [];
6217         
6218         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6219             var config = cm.config[i];
6220             
6221             var renderer = cm.getRenderer(i);
6222             var value = '';
6223             var id = false;
6224             
6225             if(typeof(renderer) !== 'undefined'){
6226                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6227             }
6228             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6229             // and are rendered into the cells after the row is rendered - using the id for the element.
6230             
6231             if(typeof(value) === 'object'){
6232                 id = Roo.id();
6233                 cellObjects.push({
6234                     container : id,
6235                     cfg : value 
6236                 })
6237             }
6238             
6239             var rowcfg = {
6240                 record: d,
6241                 rowIndex : rowIndex,
6242                 colIndex : i,
6243                 rowClass : ''
6244             };
6245
6246             this.fireEvent('rowclass', this, rowcfg);
6247             
6248             var td = {
6249                 tag: 'td',
6250                 cls : rowcfg.rowClass,
6251                 style: '',
6252                 html: (typeof(value) === 'object') ? '' : value
6253             };
6254             
6255             if (id) {
6256                 td.id = id;
6257             }
6258             
6259             if(typeof(config.colspan) != 'undefined'){
6260                 td.colspan = config.colspan;
6261             }
6262             
6263             if(typeof(config.hidden) != 'undefined' && config.hidden){
6264                 td.style += ' display:none;';
6265             }
6266             
6267             if(typeof(config.align) != 'undefined' && config.align.length){
6268                 td.style += ' text-align:' + config.align + ';';
6269             }
6270             
6271             if(typeof(config.width) != 'undefined'){
6272                 td.style += ' width:' +  config.width + 'px;';
6273             }
6274             
6275             if(typeof(config.cursor) != 'undefined'){
6276                 td.style += ' cursor:' +  config.cursor + ';';
6277             }
6278             
6279             if(typeof(config.cls) != 'undefined'){
6280                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6281             }
6282             
6283             ['xs','sm','md','lg'].map(function(size){
6284                 
6285                 if(typeof(config[size]) == 'undefined'){
6286                     return;
6287                 }
6288                 
6289                 if (!config[size]) { // 0 = hidden
6290                     td.cls += ' hidden-' + size;
6291                     return;
6292                 }
6293                 
6294                 td.cls += ' col-' + size + '-' + config[size];
6295
6296             });
6297              
6298             row.cn.push(td);
6299            
6300         }
6301         
6302         row.cellObjects = cellObjects;
6303         
6304         return row;
6305           
6306     },
6307     
6308     
6309     
6310     onBeforeLoad : function()
6311     {
6312         //Roo.log('ds onBeforeLoad');
6313         
6314         //this.clear();
6315         
6316         //if(this.loadMask){
6317         //    this.maskEl.show();
6318         //}
6319     },
6320      /**
6321      * Remove all rows
6322      */
6323     clear : function()
6324     {
6325         this.el.select('tbody', true).first().dom.innerHTML = '';
6326     },
6327     /**
6328      * Show or hide a row.
6329      * @param {Number} rowIndex to show or hide
6330      * @param {Boolean} state hide
6331      */
6332     setRowVisibility : function(rowIndex, state)
6333     {
6334         var bt = this.mainBody.dom;
6335         
6336         var rows = this.el.select('tbody > tr', true).elements;
6337         
6338         if(typeof(rows[rowIndex]) == 'undefined'){
6339             return;
6340         }
6341         rows[rowIndex].dom.style.display = state ? '' : 'none';
6342     },
6343     
6344     
6345     getSelectionModel : function(){
6346         if(!this.selModel){
6347             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6348         }
6349         return this.selModel;
6350     },
6351     /*
6352      * Render the Roo.bootstrap object from renderder
6353      */
6354     renderCellObject : function(r)
6355     {
6356         var _this = this;
6357         
6358         var t = r.cfg.render(r.container);
6359         
6360         if(r.cfg.cn){
6361             Roo.each(r.cfg.cn, function(c){
6362                 var child = {
6363                     container: t.getChildContainer(),
6364                     cfg: c
6365                 };
6366                 _this.renderCellObject(child);
6367             })
6368         }
6369     },
6370     
6371     getRowIndex : function(row)
6372     {
6373         var rowIndex = -1;
6374         
6375         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6376             if(el != row){
6377                 return;
6378             }
6379             
6380             rowIndex = index;
6381         });
6382         
6383         return rowIndex;
6384     }
6385    
6386 });
6387
6388  
6389
6390  /*
6391  * - LGPL
6392  *
6393  * table cell
6394  * 
6395  */
6396
6397 /**
6398  * @class Roo.bootstrap.TableCell
6399  * @extends Roo.bootstrap.Component
6400  * Bootstrap TableCell class
6401  * @cfg {String} html cell contain text
6402  * @cfg {String} cls cell class
6403  * @cfg {String} tag cell tag (td|th) default td
6404  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6405  * @cfg {String} align Aligns the content in a cell
6406  * @cfg {String} axis Categorizes cells
6407  * @cfg {String} bgcolor Specifies the background color of a cell
6408  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6409  * @cfg {Number} colspan Specifies the number of columns a cell should span
6410  * @cfg {String} headers Specifies one or more header cells a cell is related to
6411  * @cfg {Number} height Sets the height of a cell
6412  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6413  * @cfg {Number} rowspan Sets the number of rows a cell should span
6414  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6415  * @cfg {String} valign Vertical aligns the content in a cell
6416  * @cfg {Number} width Specifies the width of a cell
6417  * 
6418  * @constructor
6419  * Create a new TableCell
6420  * @param {Object} config The config object
6421  */
6422
6423 Roo.bootstrap.TableCell = function(config){
6424     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6425 };
6426
6427 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6428     
6429     html: false,
6430     cls: false,
6431     tag: false,
6432     abbr: false,
6433     align: false,
6434     axis: false,
6435     bgcolor: false,
6436     charoff: false,
6437     colspan: false,
6438     headers: false,
6439     height: false,
6440     nowrap: false,
6441     rowspan: false,
6442     scope: false,
6443     valign: false,
6444     width: false,
6445     
6446     
6447     getAutoCreate : function(){
6448         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6449         
6450         cfg = {
6451             tag: 'td'
6452         };
6453         
6454         if(this.tag){
6455             cfg.tag = this.tag;
6456         }
6457         
6458         if (this.html) {
6459             cfg.html=this.html
6460         }
6461         if (this.cls) {
6462             cfg.cls=this.cls
6463         }
6464         if (this.abbr) {
6465             cfg.abbr=this.abbr
6466         }
6467         if (this.align) {
6468             cfg.align=this.align
6469         }
6470         if (this.axis) {
6471             cfg.axis=this.axis
6472         }
6473         if (this.bgcolor) {
6474             cfg.bgcolor=this.bgcolor
6475         }
6476         if (this.charoff) {
6477             cfg.charoff=this.charoff
6478         }
6479         if (this.colspan) {
6480             cfg.colspan=this.colspan
6481         }
6482         if (this.headers) {
6483             cfg.headers=this.headers
6484         }
6485         if (this.height) {
6486             cfg.height=this.height
6487         }
6488         if (this.nowrap) {
6489             cfg.nowrap=this.nowrap
6490         }
6491         if (this.rowspan) {
6492             cfg.rowspan=this.rowspan
6493         }
6494         if (this.scope) {
6495             cfg.scope=this.scope
6496         }
6497         if (this.valign) {
6498             cfg.valign=this.valign
6499         }
6500         if (this.width) {
6501             cfg.width=this.width
6502         }
6503         
6504         
6505         return cfg;
6506     }
6507    
6508 });
6509
6510  
6511
6512  /*
6513  * - LGPL
6514  *
6515  * table row
6516  * 
6517  */
6518
6519 /**
6520  * @class Roo.bootstrap.TableRow
6521  * @extends Roo.bootstrap.Component
6522  * Bootstrap TableRow class
6523  * @cfg {String} cls row class
6524  * @cfg {String} align Aligns the content in a table row
6525  * @cfg {String} bgcolor Specifies a background color for a table row
6526  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6527  * @cfg {String} valign Vertical aligns the content in a table row
6528  * 
6529  * @constructor
6530  * Create a new TableRow
6531  * @param {Object} config The config object
6532  */
6533
6534 Roo.bootstrap.TableRow = function(config){
6535     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6536 };
6537
6538 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6539     
6540     cls: false,
6541     align: false,
6542     bgcolor: false,
6543     charoff: false,
6544     valign: false,
6545     
6546     getAutoCreate : function(){
6547         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6548         
6549         cfg = {
6550             tag: 'tr'
6551         };
6552             
6553         if(this.cls){
6554             cfg.cls = this.cls;
6555         }
6556         if(this.align){
6557             cfg.align = this.align;
6558         }
6559         if(this.bgcolor){
6560             cfg.bgcolor = this.bgcolor;
6561         }
6562         if(this.charoff){
6563             cfg.charoff = this.charoff;
6564         }
6565         if(this.valign){
6566             cfg.valign = this.valign;
6567         }
6568         
6569         return cfg;
6570     }
6571    
6572 });
6573
6574  
6575
6576  /*
6577  * - LGPL
6578  *
6579  * table body
6580  * 
6581  */
6582
6583 /**
6584  * @class Roo.bootstrap.TableBody
6585  * @extends Roo.bootstrap.Component
6586  * Bootstrap TableBody class
6587  * @cfg {String} cls element class
6588  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6589  * @cfg {String} align Aligns the content inside the element
6590  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6591  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6592  * 
6593  * @constructor
6594  * Create a new TableBody
6595  * @param {Object} config The config object
6596  */
6597
6598 Roo.bootstrap.TableBody = function(config){
6599     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6600 };
6601
6602 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6603     
6604     cls: false,
6605     tag: false,
6606     align: false,
6607     charoff: false,
6608     valign: false,
6609     
6610     getAutoCreate : function(){
6611         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6612         
6613         cfg = {
6614             tag: 'tbody'
6615         };
6616             
6617         if (this.cls) {
6618             cfg.cls=this.cls
6619         }
6620         if(this.tag){
6621             cfg.tag = this.tag;
6622         }
6623         
6624         if(this.align){
6625             cfg.align = this.align;
6626         }
6627         if(this.charoff){
6628             cfg.charoff = this.charoff;
6629         }
6630         if(this.valign){
6631             cfg.valign = this.valign;
6632         }
6633         
6634         return cfg;
6635     }
6636     
6637     
6638 //    initEvents : function()
6639 //    {
6640 //        
6641 //        if(!this.store){
6642 //            return;
6643 //        }
6644 //        
6645 //        this.store = Roo.factory(this.store, Roo.data);
6646 //        this.store.on('load', this.onLoad, this);
6647 //        
6648 //        this.store.load();
6649 //        
6650 //    },
6651 //    
6652 //    onLoad: function () 
6653 //    {   
6654 //        this.fireEvent('load', this);
6655 //    }
6656 //    
6657 //   
6658 });
6659
6660  
6661
6662  /*
6663  * Based on:
6664  * Ext JS Library 1.1.1
6665  * Copyright(c) 2006-2007, Ext JS, LLC.
6666  *
6667  * Originally Released Under LGPL - original licence link has changed is not relivant.
6668  *
6669  * Fork - LGPL
6670  * <script type="text/javascript">
6671  */
6672
6673 // as we use this in bootstrap.
6674 Roo.namespace('Roo.form');
6675  /**
6676  * @class Roo.form.Action
6677  * Internal Class used to handle form actions
6678  * @constructor
6679  * @param {Roo.form.BasicForm} el The form element or its id
6680  * @param {Object} config Configuration options
6681  */
6682
6683  
6684  
6685 // define the action interface
6686 Roo.form.Action = function(form, options){
6687     this.form = form;
6688     this.options = options || {};
6689 };
6690 /**
6691  * Client Validation Failed
6692  * @const 
6693  */
6694 Roo.form.Action.CLIENT_INVALID = 'client';
6695 /**
6696  * Server Validation Failed
6697  * @const 
6698  */
6699 Roo.form.Action.SERVER_INVALID = 'server';
6700  /**
6701  * Connect to Server Failed
6702  * @const 
6703  */
6704 Roo.form.Action.CONNECT_FAILURE = 'connect';
6705 /**
6706  * Reading Data from Server Failed
6707  * @const 
6708  */
6709 Roo.form.Action.LOAD_FAILURE = 'load';
6710
6711 Roo.form.Action.prototype = {
6712     type : 'default',
6713     failureType : undefined,
6714     response : undefined,
6715     result : undefined,
6716
6717     // interface method
6718     run : function(options){
6719
6720     },
6721
6722     // interface method
6723     success : function(response){
6724
6725     },
6726
6727     // interface method
6728     handleResponse : function(response){
6729
6730     },
6731
6732     // default connection failure
6733     failure : function(response){
6734         
6735         this.response = response;
6736         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6737         this.form.afterAction(this, false);
6738     },
6739
6740     processResponse : function(response){
6741         this.response = response;
6742         if(!response.responseText){
6743             return true;
6744         }
6745         this.result = this.handleResponse(response);
6746         return this.result;
6747     },
6748
6749     // utility functions used internally
6750     getUrl : function(appendParams){
6751         var url = this.options.url || this.form.url || this.form.el.dom.action;
6752         if(appendParams){
6753             var p = this.getParams();
6754             if(p){
6755                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6756             }
6757         }
6758         return url;
6759     },
6760
6761     getMethod : function(){
6762         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6763     },
6764
6765     getParams : function(){
6766         var bp = this.form.baseParams;
6767         var p = this.options.params;
6768         if(p){
6769             if(typeof p == "object"){
6770                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6771             }else if(typeof p == 'string' && bp){
6772                 p += '&' + Roo.urlEncode(bp);
6773             }
6774         }else if(bp){
6775             p = Roo.urlEncode(bp);
6776         }
6777         return p;
6778     },
6779
6780     createCallback : function(){
6781         return {
6782             success: this.success,
6783             failure: this.failure,
6784             scope: this,
6785             timeout: (this.form.timeout*1000),
6786             upload: this.form.fileUpload ? this.success : undefined
6787         };
6788     }
6789 };
6790
6791 Roo.form.Action.Submit = function(form, options){
6792     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6793 };
6794
6795 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6796     type : 'submit',
6797
6798     haveProgress : false,
6799     uploadComplete : false,
6800     
6801     // uploadProgress indicator.
6802     uploadProgress : function()
6803     {
6804         if (!this.form.progressUrl) {
6805             return;
6806         }
6807         
6808         if (!this.haveProgress) {
6809             Roo.MessageBox.progress("Uploading", "Uploading");
6810         }
6811         if (this.uploadComplete) {
6812            Roo.MessageBox.hide();
6813            return;
6814         }
6815         
6816         this.haveProgress = true;
6817    
6818         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6819         
6820         var c = new Roo.data.Connection();
6821         c.request({
6822             url : this.form.progressUrl,
6823             params: {
6824                 id : uid
6825             },
6826             method: 'GET',
6827             success : function(req){
6828                //console.log(data);
6829                 var rdata = false;
6830                 var edata;
6831                 try  {
6832                    rdata = Roo.decode(req.responseText)
6833                 } catch (e) {
6834                     Roo.log("Invalid data from server..");
6835                     Roo.log(edata);
6836                     return;
6837                 }
6838                 if (!rdata || !rdata.success) {
6839                     Roo.log(rdata);
6840                     Roo.MessageBox.alert(Roo.encode(rdata));
6841                     return;
6842                 }
6843                 var data = rdata.data;
6844                 
6845                 if (this.uploadComplete) {
6846                    Roo.MessageBox.hide();
6847                    return;
6848                 }
6849                    
6850                 if (data){
6851                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6852                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6853                     );
6854                 }
6855                 this.uploadProgress.defer(2000,this);
6856             },
6857        
6858             failure: function(data) {
6859                 Roo.log('progress url failed ');
6860                 Roo.log(data);
6861             },
6862             scope : this
6863         });
6864            
6865     },
6866     
6867     
6868     run : function()
6869     {
6870         // run get Values on the form, so it syncs any secondary forms.
6871         this.form.getValues();
6872         
6873         var o = this.options;
6874         var method = this.getMethod();
6875         var isPost = method == 'POST';
6876         if(o.clientValidation === false || this.form.isValid()){
6877             
6878             if (this.form.progressUrl) {
6879                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6880                     (new Date() * 1) + '' + Math.random());
6881                     
6882             } 
6883             
6884             
6885             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6886                 form:this.form.el.dom,
6887                 url:this.getUrl(!isPost),
6888                 method: method,
6889                 params:isPost ? this.getParams() : null,
6890                 isUpload: this.form.fileUpload
6891             }));
6892             
6893             this.uploadProgress();
6894
6895         }else if (o.clientValidation !== false){ // client validation failed
6896             this.failureType = Roo.form.Action.CLIENT_INVALID;
6897             this.form.afterAction(this, false);
6898         }
6899     },
6900
6901     success : function(response)
6902     {
6903         this.uploadComplete= true;
6904         if (this.haveProgress) {
6905             Roo.MessageBox.hide();
6906         }
6907         
6908         
6909         var result = this.processResponse(response);
6910         if(result === true || result.success){
6911             this.form.afterAction(this, true);
6912             return;
6913         }
6914         if(result.errors){
6915             this.form.markInvalid(result.errors);
6916             this.failureType = Roo.form.Action.SERVER_INVALID;
6917         }
6918         this.form.afterAction(this, false);
6919     },
6920     failure : function(response)
6921     {
6922         this.uploadComplete= true;
6923         if (this.haveProgress) {
6924             Roo.MessageBox.hide();
6925         }
6926         
6927         this.response = response;
6928         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6929         this.form.afterAction(this, false);
6930     },
6931     
6932     handleResponse : function(response){
6933         if(this.form.errorReader){
6934             var rs = this.form.errorReader.read(response);
6935             var errors = [];
6936             if(rs.records){
6937                 for(var i = 0, len = rs.records.length; i < len; i++) {
6938                     var r = rs.records[i];
6939                     errors[i] = r.data;
6940                 }
6941             }
6942             if(errors.length < 1){
6943                 errors = null;
6944             }
6945             return {
6946                 success : rs.success,
6947                 errors : errors
6948             };
6949         }
6950         var ret = false;
6951         try {
6952             ret = Roo.decode(response.responseText);
6953         } catch (e) {
6954             ret = {
6955                 success: false,
6956                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6957                 errors : []
6958             };
6959         }
6960         return ret;
6961         
6962     }
6963 });
6964
6965
6966 Roo.form.Action.Load = function(form, options){
6967     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6968     this.reader = this.form.reader;
6969 };
6970
6971 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6972     type : 'load',
6973
6974     run : function(){
6975         
6976         Roo.Ajax.request(Roo.apply(
6977                 this.createCallback(), {
6978                     method:this.getMethod(),
6979                     url:this.getUrl(false),
6980                     params:this.getParams()
6981         }));
6982     },
6983
6984     success : function(response){
6985         
6986         var result = this.processResponse(response);
6987         if(result === true || !result.success || !result.data){
6988             this.failureType = Roo.form.Action.LOAD_FAILURE;
6989             this.form.afterAction(this, false);
6990             return;
6991         }
6992         this.form.clearInvalid();
6993         this.form.setValues(result.data);
6994         this.form.afterAction(this, true);
6995     },
6996
6997     handleResponse : function(response){
6998         if(this.form.reader){
6999             var rs = this.form.reader.read(response);
7000             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7001             return {
7002                 success : rs.success,
7003                 data : data
7004             };
7005         }
7006         return Roo.decode(response.responseText);
7007     }
7008 });
7009
7010 Roo.form.Action.ACTION_TYPES = {
7011     'load' : Roo.form.Action.Load,
7012     'submit' : Roo.form.Action.Submit
7013 };/*
7014  * - LGPL
7015  *
7016  * form
7017  * 
7018  */
7019
7020 /**
7021  * @class Roo.bootstrap.Form
7022  * @extends Roo.bootstrap.Component
7023  * Bootstrap Form class
7024  * @cfg {String} method  GET | POST (default POST)
7025  * @cfg {String} labelAlign top | left (default top)
7026  * @cfg {String} align left  | right - for navbars
7027  * @cfg {Boolean} loadMask load mask when submit (default true)
7028
7029  * 
7030  * @constructor
7031  * Create a new Form
7032  * @param {Object} config The config object
7033  */
7034
7035
7036 Roo.bootstrap.Form = function(config){
7037     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7038     this.addEvents({
7039         /**
7040          * @event clientvalidation
7041          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7042          * @param {Form} this
7043          * @param {Boolean} valid true if the form has passed client-side validation
7044          */
7045         clientvalidation: true,
7046         /**
7047          * @event beforeaction
7048          * Fires before any action is performed. Return false to cancel the action.
7049          * @param {Form} this
7050          * @param {Action} action The action to be performed
7051          */
7052         beforeaction: true,
7053         /**
7054          * @event actionfailed
7055          * Fires when an action fails.
7056          * @param {Form} this
7057          * @param {Action} action The action that failed
7058          */
7059         actionfailed : true,
7060         /**
7061          * @event actioncomplete
7062          * Fires when an action is completed.
7063          * @param {Form} this
7064          * @param {Action} action The action that completed
7065          */
7066         actioncomplete : true
7067     });
7068     
7069 };
7070
7071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7072       
7073      /**
7074      * @cfg {String} method
7075      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7076      */
7077     method : 'POST',
7078     /**
7079      * @cfg {String} url
7080      * The URL to use for form actions if one isn't supplied in the action options.
7081      */
7082     /**
7083      * @cfg {Boolean} fileUpload
7084      * Set to true if this form is a file upload.
7085      */
7086      
7087     /**
7088      * @cfg {Object} baseParams
7089      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7090      */
7091       
7092     /**
7093      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7094      */
7095     timeout: 30,
7096     /**
7097      * @cfg {Sting} align (left|right) for navbar forms
7098      */
7099     align : 'left',
7100
7101     // private
7102     activeAction : null,
7103  
7104     /**
7105      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7106      * element by passing it or its id or mask the form itself by passing in true.
7107      * @type Mixed
7108      */
7109     waitMsgTarget : false,
7110     
7111     loadMask : true,
7112     
7113     getAutoCreate : function(){
7114         
7115         var cfg = {
7116             tag: 'form',
7117             method : this.method || 'POST',
7118             id : this.id || Roo.id(),
7119             cls : ''
7120         };
7121         if (this.parent().xtype.match(/^Nav/)) {
7122             cfg.cls = 'navbar-form navbar-' + this.align;
7123             
7124         }
7125         
7126         if (this.labelAlign == 'left' ) {
7127             cfg.cls += ' form-horizontal';
7128         }
7129         
7130         
7131         return cfg;
7132     },
7133     initEvents : function()
7134     {
7135         this.el.on('submit', this.onSubmit, this);
7136         // this was added as random key presses on the form where triggering form submit.
7137         this.el.on('keypress', function(e) {
7138             if (e.getCharCode() != 13) {
7139                 return true;
7140             }
7141             // we might need to allow it for textareas.. and some other items.
7142             // check e.getTarget().
7143             
7144             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7145                 return true;
7146             }
7147         
7148             Roo.log("keypress blocked");
7149             
7150             e.preventDefault();
7151             return false;
7152         });
7153         
7154     },
7155     // private
7156     onSubmit : function(e){
7157         e.stopEvent();
7158     },
7159     
7160      /**
7161      * Returns true if client-side validation on the form is successful.
7162      * @return Boolean
7163      */
7164     isValid : function(){
7165         var items = this.getItems();
7166         var valid = true;
7167         items.each(function(f){
7168            if(!f.validate()){
7169                valid = false;
7170                
7171            }
7172         });
7173         return valid;
7174     },
7175     /**
7176      * Returns true if any fields in this form have changed since their original load.
7177      * @return Boolean
7178      */
7179     isDirty : function(){
7180         var dirty = false;
7181         var items = this.getItems();
7182         items.each(function(f){
7183            if(f.isDirty()){
7184                dirty = true;
7185                return false;
7186            }
7187            return true;
7188         });
7189         return dirty;
7190     },
7191      /**
7192      * Performs a predefined action (submit or load) or custom actions you define on this form.
7193      * @param {String} actionName The name of the action type
7194      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7195      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7196      * accept other config options):
7197      * <pre>
7198 Property          Type             Description
7199 ----------------  ---------------  ----------------------------------------------------------------------------------
7200 url               String           The url for the action (defaults to the form's url)
7201 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7202 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7203 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7204                                    validate the form on the client (defaults to false)
7205      * </pre>
7206      * @return {BasicForm} this
7207      */
7208     doAction : function(action, options){
7209         if(typeof action == 'string'){
7210             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7211         }
7212         if(this.fireEvent('beforeaction', this, action) !== false){
7213             this.beforeAction(action);
7214             action.run.defer(100, action);
7215         }
7216         return this;
7217     },
7218     
7219     // private
7220     beforeAction : function(action){
7221         var o = action.options;
7222         
7223         if(this.loadMask){
7224             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7225         }
7226         // not really supported yet.. ??
7227         
7228         //if(this.waitMsgTarget === true){
7229         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7230         //}else if(this.waitMsgTarget){
7231         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7232         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7233         //}else {
7234         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7235        // }
7236          
7237     },
7238
7239     // private
7240     afterAction : function(action, success){
7241         this.activeAction = null;
7242         var o = action.options;
7243         
7244         //if(this.waitMsgTarget === true){
7245             this.el.unmask();
7246         //}else if(this.waitMsgTarget){
7247         //    this.waitMsgTarget.unmask();
7248         //}else{
7249         //    Roo.MessageBox.updateProgress(1);
7250         //    Roo.MessageBox.hide();
7251        // }
7252         // 
7253         if(success){
7254             if(o.reset){
7255                 this.reset();
7256             }
7257             Roo.callback(o.success, o.scope, [this, action]);
7258             this.fireEvent('actioncomplete', this, action);
7259             
7260         }else{
7261             
7262             // failure condition..
7263             // we have a scenario where updates need confirming.
7264             // eg. if a locking scenario exists..
7265             // we look for { errors : { needs_confirm : true }} in the response.
7266             if (
7267                 (typeof(action.result) != 'undefined')  &&
7268                 (typeof(action.result.errors) != 'undefined')  &&
7269                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7270            ){
7271                 var _t = this;
7272                 Roo.log("not supported yet");
7273                  /*
7274                 
7275                 Roo.MessageBox.confirm(
7276                     "Change requires confirmation",
7277                     action.result.errorMsg,
7278                     function(r) {
7279                         if (r != 'yes') {
7280                             return;
7281                         }
7282                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7283                     }
7284                     
7285                 );
7286                 */
7287                 
7288                 
7289                 return;
7290             }
7291             
7292             Roo.callback(o.failure, o.scope, [this, action]);
7293             // show an error message if no failed handler is set..
7294             if (!this.hasListener('actionfailed')) {
7295                 Roo.log("need to add dialog support");
7296                 /*
7297                 Roo.MessageBox.alert("Error",
7298                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7299                         action.result.errorMsg :
7300                         "Saving Failed, please check your entries or try again"
7301                 );
7302                 */
7303             }
7304             
7305             this.fireEvent('actionfailed', this, action);
7306         }
7307         
7308     },
7309     /**
7310      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7311      * @param {String} id The value to search for
7312      * @return Field
7313      */
7314     findField : function(id){
7315         var items = this.getItems();
7316         var field = items.get(id);
7317         if(!field){
7318              items.each(function(f){
7319                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7320                     field = f;
7321                     return false;
7322                 }
7323                 return true;
7324             });
7325         }
7326         return field || null;
7327     },
7328      /**
7329      * Mark fields in this form invalid in bulk.
7330      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7331      * @return {BasicForm} this
7332      */
7333     markInvalid : function(errors){
7334         if(errors instanceof Array){
7335             for(var i = 0, len = errors.length; i < len; i++){
7336                 var fieldError = errors[i];
7337                 var f = this.findField(fieldError.id);
7338                 if(f){
7339                     f.markInvalid(fieldError.msg);
7340                 }
7341             }
7342         }else{
7343             var field, id;
7344             for(id in errors){
7345                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7346                     field.markInvalid(errors[id]);
7347                 }
7348             }
7349         }
7350         //Roo.each(this.childForms || [], function (f) {
7351         //    f.markInvalid(errors);
7352         //});
7353         
7354         return this;
7355     },
7356
7357     /**
7358      * Set values for fields in this form in bulk.
7359      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7360      * @return {BasicForm} this
7361      */
7362     setValues : function(values){
7363         if(values instanceof Array){ // array of objects
7364             for(var i = 0, len = values.length; i < len; i++){
7365                 var v = values[i];
7366                 var f = this.findField(v.id);
7367                 if(f){
7368                     f.setValue(v.value);
7369                     if(this.trackResetOnLoad){
7370                         f.originalValue = f.getValue();
7371                     }
7372                 }
7373             }
7374         }else{ // object hash
7375             var field, id;
7376             for(id in values){
7377                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7378                     
7379                     if (field.setFromData && 
7380                         field.valueField && 
7381                         field.displayField &&
7382                         // combos' with local stores can 
7383                         // be queried via setValue()
7384                         // to set their value..
7385                         (field.store && !field.store.isLocal)
7386                         ) {
7387                         // it's a combo
7388                         var sd = { };
7389                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7390                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7391                         field.setFromData(sd);
7392                         
7393                     } else {
7394                         field.setValue(values[id]);
7395                     }
7396                     
7397                     
7398                     if(this.trackResetOnLoad){
7399                         field.originalValue = field.getValue();
7400                     }
7401                 }
7402             }
7403         }
7404          
7405         //Roo.each(this.childForms || [], function (f) {
7406         //    f.setValues(values);
7407         //});
7408                 
7409         return this;
7410     },
7411
7412     /**
7413      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7414      * they are returned as an array.
7415      * @param {Boolean} asString
7416      * @return {Object}
7417      */
7418     getValues : function(asString){
7419         //if (this.childForms) {
7420             // copy values from the child forms
7421         //    Roo.each(this.childForms, function (f) {
7422         //        this.setValues(f.getValues());
7423         //    }, this);
7424         //}
7425         
7426         
7427         
7428         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7429         if(asString === true){
7430             return fs;
7431         }
7432         return Roo.urlDecode(fs);
7433     },
7434     
7435     /**
7436      * Returns the fields in this form as an object with key/value pairs. 
7437      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7438      * @return {Object}
7439      */
7440     getFieldValues : function(with_hidden)
7441     {
7442         var items = this.getItems();
7443         var ret = {};
7444         items.each(function(f){
7445             if (!f.getName()) {
7446                 return;
7447             }
7448             var v = f.getValue();
7449             if (f.inputType =='radio') {
7450                 if (typeof(ret[f.getName()]) == 'undefined') {
7451                     ret[f.getName()] = ''; // empty..
7452                 }
7453                 
7454                 if (!f.el.dom.checked) {
7455                     return;
7456                     
7457                 }
7458                 v = f.el.dom.value;
7459                 
7460             }
7461             
7462             // not sure if this supported any more..
7463             if ((typeof(v) == 'object') && f.getRawValue) {
7464                 v = f.getRawValue() ; // dates..
7465             }
7466             // combo boxes where name != hiddenName...
7467             if (f.name != f.getName()) {
7468                 ret[f.name] = f.getRawValue();
7469             }
7470             ret[f.getName()] = v;
7471         });
7472         
7473         return ret;
7474     },
7475
7476     /**
7477      * Clears all invalid messages in this form.
7478      * @return {BasicForm} this
7479      */
7480     clearInvalid : function(){
7481         var items = this.getItems();
7482         
7483         items.each(function(f){
7484            f.clearInvalid();
7485         });
7486         
7487         
7488         
7489         return this;
7490     },
7491
7492     /**
7493      * Resets this form.
7494      * @return {BasicForm} this
7495      */
7496     reset : function(){
7497         var items = this.getItems();
7498         items.each(function(f){
7499             f.reset();
7500         });
7501         
7502         Roo.each(this.childForms || [], function (f) {
7503             f.reset();
7504         });
7505        
7506         
7507         return this;
7508     },
7509     getItems : function()
7510     {
7511         var r=new Roo.util.MixedCollection(false, function(o){
7512             return o.id || (o.id = Roo.id());
7513         });
7514         var iter = function(el) {
7515             if (el.inputEl) {
7516                 r.add(el);
7517             }
7518             if (!el.items) {
7519                 return;
7520             }
7521             Roo.each(el.items,function(e) {
7522                 iter(e);
7523             });
7524             
7525             
7526         };
7527         
7528         iter(this);
7529         return r;
7530         
7531         
7532         
7533         
7534     }
7535     
7536 });
7537
7538  
7539 /*
7540  * Based on:
7541  * Ext JS Library 1.1.1
7542  * Copyright(c) 2006-2007, Ext JS, LLC.
7543  *
7544  * Originally Released Under LGPL - original licence link has changed is not relivant.
7545  *
7546  * Fork - LGPL
7547  * <script type="text/javascript">
7548  */
7549 /**
7550  * @class Roo.form.VTypes
7551  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7552  * @singleton
7553  */
7554 Roo.form.VTypes = function(){
7555     // closure these in so they are only created once.
7556     var alpha = /^[a-zA-Z_]+$/;
7557     var alphanum = /^[a-zA-Z0-9_]+$/;
7558     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7559     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7560
7561     // All these messages and functions are configurable
7562     return {
7563         /**
7564          * The function used to validate email addresses
7565          * @param {String} value The email address
7566          */
7567         'email' : function(v){
7568             return email.test(v);
7569         },
7570         /**
7571          * The error text to display when the email validation function returns false
7572          * @type String
7573          */
7574         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7575         /**
7576          * The keystroke filter mask to be applied on email input
7577          * @type RegExp
7578          */
7579         'emailMask' : /[a-z0-9_\.\-@]/i,
7580
7581         /**
7582          * The function used to validate URLs
7583          * @param {String} value The URL
7584          */
7585         'url' : function(v){
7586             return url.test(v);
7587         },
7588         /**
7589          * The error text to display when the url validation function returns false
7590          * @type String
7591          */
7592         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7593         
7594         /**
7595          * The function used to validate alpha values
7596          * @param {String} value The value
7597          */
7598         'alpha' : function(v){
7599             return alpha.test(v);
7600         },
7601         /**
7602          * The error text to display when the alpha validation function returns false
7603          * @type String
7604          */
7605         'alphaText' : 'This field should only contain letters and _',
7606         /**
7607          * The keystroke filter mask to be applied on alpha input
7608          * @type RegExp
7609          */
7610         'alphaMask' : /[a-z_]/i,
7611
7612         /**
7613          * The function used to validate alphanumeric values
7614          * @param {String} value The value
7615          */
7616         'alphanum' : function(v){
7617             return alphanum.test(v);
7618         },
7619         /**
7620          * The error text to display when the alphanumeric validation function returns false
7621          * @type String
7622          */
7623         'alphanumText' : 'This field should only contain letters, numbers and _',
7624         /**
7625          * The keystroke filter mask to be applied on alphanumeric input
7626          * @type RegExp
7627          */
7628         'alphanumMask' : /[a-z0-9_]/i
7629     };
7630 }();/*
7631  * - LGPL
7632  *
7633  * Input
7634  * 
7635  */
7636
7637 /**
7638  * @class Roo.bootstrap.Input
7639  * @extends Roo.bootstrap.Component
7640  * Bootstrap Input class
7641  * @cfg {Boolean} disabled is it disabled
7642  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7643  * @cfg {String} name name of the input
7644  * @cfg {string} fieldLabel - the label associated
7645  * @cfg {string} placeholder - placeholder to put in text.
7646  * @cfg {string}  before - input group add on before
7647  * @cfg {string} after - input group add on after
7648  * @cfg {string} size - (lg|sm) or leave empty..
7649  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7650  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7651  * @cfg {Number} md colspan out of 12 for computer-sized screens
7652  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7653  * @cfg {string} value default value of the input
7654  * @cfg {Number} labelWidth set the width of label (0-12)
7655  * @cfg {String} labelAlign (top|left)
7656  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7657  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7658
7659  * @cfg {String} align (left|center|right) Default left
7660  * @cfg {Boolean} forceFeedback (true|false) Default false
7661  * 
7662  * 
7663  * 
7664  * 
7665  * @constructor
7666  * Create a new Input
7667  * @param {Object} config The config object
7668  */
7669
7670 Roo.bootstrap.Input = function(config){
7671     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7672    
7673         this.addEvents({
7674             /**
7675              * @event focus
7676              * Fires when this field receives input focus.
7677              * @param {Roo.form.Field} this
7678              */
7679             focus : true,
7680             /**
7681              * @event blur
7682              * Fires when this field loses input focus.
7683              * @param {Roo.form.Field} this
7684              */
7685             blur : true,
7686             /**
7687              * @event specialkey
7688              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7689              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7690              * @param {Roo.form.Field} this
7691              * @param {Roo.EventObject} e The event object
7692              */
7693             specialkey : true,
7694             /**
7695              * @event change
7696              * Fires just before the field blurs if the field value has changed.
7697              * @param {Roo.form.Field} this
7698              * @param {Mixed} newValue The new value
7699              * @param {Mixed} oldValue The original value
7700              */
7701             change : true,
7702             /**
7703              * @event invalid
7704              * Fires after the field has been marked as invalid.
7705              * @param {Roo.form.Field} this
7706              * @param {String} msg The validation message
7707              */
7708             invalid : true,
7709             /**
7710              * @event valid
7711              * Fires after the field has been validated with no errors.
7712              * @param {Roo.form.Field} this
7713              */
7714             valid : true,
7715              /**
7716              * @event keyup
7717              * Fires after the key up
7718              * @param {Roo.form.Field} this
7719              * @param {Roo.EventObject}  e The event Object
7720              */
7721             keyup : true
7722         });
7723 };
7724
7725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7726      /**
7727      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7728       automatic validation (defaults to "keyup").
7729      */
7730     validationEvent : "keyup",
7731      /**
7732      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7733      */
7734     validateOnBlur : true,
7735     /**
7736      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7737      */
7738     validationDelay : 250,
7739      /**
7740      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7741      */
7742     focusClass : "x-form-focus",  // not needed???
7743     
7744        
7745     /**
7746      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7747      */
7748     invalidClass : "has-warning",
7749     
7750     /**
7751      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7752      */
7753     validClass : "has-success",
7754     
7755     /**
7756      * @cfg {Boolean} hasFeedback (true|false) default true
7757      */
7758     hasFeedback : true,
7759     
7760     /**
7761      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7762      */
7763     invalidFeedbackClass : "glyphicon-warning-sign",
7764     
7765     /**
7766      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7767      */
7768     validFeedbackClass : "glyphicon-ok",
7769     
7770     /**
7771      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7772      */
7773     selectOnFocus : false,
7774     
7775      /**
7776      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7777      */
7778     maskRe : null,
7779        /**
7780      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7781      */
7782     vtype : null,
7783     
7784       /**
7785      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7786      */
7787     disableKeyFilter : false,
7788     
7789        /**
7790      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7791      */
7792     disabled : false,
7793      /**
7794      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7795      */
7796     allowBlank : true,
7797     /**
7798      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7799      */
7800     blankText : "This field is required",
7801     
7802      /**
7803      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7804      */
7805     minLength : 0,
7806     /**
7807      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7808      */
7809     maxLength : Number.MAX_VALUE,
7810     /**
7811      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7812      */
7813     minLengthText : "The minimum length for this field is {0}",
7814     /**
7815      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7816      */
7817     maxLengthText : "The maximum length for this field is {0}",
7818   
7819     
7820     /**
7821      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7822      * If available, this function will be called only after the basic validators all return true, and will be passed the
7823      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7824      */
7825     validator : null,
7826     /**
7827      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7828      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7829      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7830      */
7831     regex : null,
7832     /**
7833      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7834      */
7835     regexText : "",
7836     
7837     autocomplete: false,
7838     
7839     
7840     fieldLabel : '',
7841     inputType : 'text',
7842     
7843     name : false,
7844     placeholder: false,
7845     before : false,
7846     after : false,
7847     size : false,
7848     hasFocus : false,
7849     preventMark: false,
7850     isFormField : true,
7851     value : '',
7852     labelWidth : 2,
7853     labelAlign : false,
7854     readOnly : false,
7855     align : false,
7856     formatedValue : false,
7857     forceFeedback : false,
7858     
7859     parentLabelAlign : function()
7860     {
7861         var parent = this;
7862         while (parent.parent()) {
7863             parent = parent.parent();
7864             if (typeof(parent.labelAlign) !='undefined') {
7865                 return parent.labelAlign;
7866             }
7867         }
7868         return 'left';
7869         
7870     },
7871     
7872     getAutoCreate : function(){
7873         
7874         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7875         
7876         var id = Roo.id();
7877         
7878         var cfg = {};
7879         
7880         if(this.inputType != 'hidden'){
7881             cfg.cls = 'form-group' //input-group
7882         }
7883         
7884         var input =  {
7885             tag: 'input',
7886             id : id,
7887             type : this.inputType,
7888             value : this.value,
7889             cls : 'form-control',
7890             placeholder : this.placeholder || '',
7891             autocomplete : this.autocomplete || 'new-password'
7892         };
7893         
7894         
7895         if(this.align){
7896             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7897         }
7898         
7899         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7900             input.maxLength = this.maxLength;
7901         }
7902         
7903         if (this.disabled) {
7904             input.disabled=true;
7905         }
7906         
7907         if (this.readOnly) {
7908             input.readonly=true;
7909         }
7910         
7911         if (this.name) {
7912             input.name = this.name;
7913         }
7914         if (this.size) {
7915             input.cls += ' input-' + this.size;
7916         }
7917         var settings=this;
7918         ['xs','sm','md','lg'].map(function(size){
7919             if (settings[size]) {
7920                 cfg.cls += ' col-' + size + '-' + settings[size];
7921             }
7922         });
7923         
7924         var inputblock = input;
7925         
7926         var feedback = {
7927             tag: 'span',
7928             cls: 'glyphicon form-control-feedback'
7929         };
7930             
7931         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7932             
7933             inputblock = {
7934                 cls : 'has-feedback',
7935                 cn :  [
7936                     input,
7937                     feedback
7938                 ] 
7939             };  
7940         }
7941         
7942         if (this.before || this.after) {
7943             
7944             inputblock = {
7945                 cls : 'input-group',
7946                 cn :  [] 
7947             };
7948             
7949             if (this.before && typeof(this.before) == 'string') {
7950                 
7951                 inputblock.cn.push({
7952                     tag :'span',
7953                     cls : 'roo-input-before input-group-addon',
7954                     html : this.before
7955                 });
7956             }
7957             if (this.before && typeof(this.before) == 'object') {
7958                 this.before = Roo.factory(this.before);
7959                 Roo.log(this.before);
7960                 inputblock.cn.push({
7961                     tag :'span',
7962                     cls : 'roo-input-before input-group-' +
7963                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7964                 });
7965             }
7966             
7967             inputblock.cn.push(input);
7968             
7969             if (this.after && typeof(this.after) == 'string') {
7970                 inputblock.cn.push({
7971                     tag :'span',
7972                     cls : 'roo-input-after input-group-addon',
7973                     html : this.after
7974                 });
7975             }
7976             if (this.after && typeof(this.after) == 'object') {
7977                 this.after = Roo.factory(this.after);
7978                 Roo.log(this.after);
7979                 inputblock.cn.push({
7980                     tag :'span',
7981                     cls : 'roo-input-after input-group-' +
7982                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7983                 });
7984             }
7985             
7986             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7987                 inputblock.cls += ' has-feedback';
7988                 inputblock.cn.push(feedback);
7989             }
7990         };
7991         
7992         if (align ==='left' && this.fieldLabel.length) {
7993                 Roo.log("left and has label");
7994                 cfg.cn = [
7995                     
7996                     {
7997                         tag: 'label',
7998                         'for' :  id,
7999                         cls : 'control-label col-sm-' + this.labelWidth,
8000                         html : this.fieldLabel
8001                         
8002                     },
8003                     {
8004                         cls : "col-sm-" + (12 - this.labelWidth), 
8005                         cn: [
8006                             inputblock
8007                         ]
8008                     }
8009                     
8010                 ];
8011         } else if ( this.fieldLabel.length) {
8012                 Roo.log(" label");
8013                  cfg.cn = [
8014                    
8015                     {
8016                         tag: 'label',
8017                         //cls : 'input-group-addon',
8018                         html : this.fieldLabel
8019                         
8020                     },
8021                     
8022                     inputblock
8023                     
8024                 ];
8025
8026         } else {
8027             
8028                 Roo.log(" no label && no align");
8029                 cfg.cn = [
8030                     
8031                         inputblock
8032                     
8033                 ];
8034                 
8035                 
8036         };
8037         Roo.log('input-parentType: ' + this.parentType);
8038         
8039         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8040            cfg.cls += ' navbar-form';
8041            Roo.log(cfg);
8042         }
8043         
8044         return cfg;
8045         
8046     },
8047     /**
8048      * return the real input element.
8049      */
8050     inputEl: function ()
8051     {
8052         return this.el.select('input.form-control',true).first();
8053     },
8054     
8055     tooltipEl : function()
8056     {
8057         return this.inputEl();
8058     },
8059     
8060     setDisabled : function(v)
8061     {
8062         var i  = this.inputEl().dom;
8063         if (!v) {
8064             i.removeAttribute('disabled');
8065             return;
8066             
8067         }
8068         i.setAttribute('disabled','true');
8069     },
8070     initEvents : function()
8071     {
8072           
8073         this.inputEl().on("keydown" , this.fireKey,  this);
8074         this.inputEl().on("focus", this.onFocus,  this);
8075         this.inputEl().on("blur", this.onBlur,  this);
8076         
8077         this.inputEl().relayEvent('keyup', this);
8078  
8079         // reference to original value for reset
8080         this.originalValue = this.getValue();
8081         //Roo.form.TextField.superclass.initEvents.call(this);
8082         if(this.validationEvent == 'keyup'){
8083             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8084             this.inputEl().on('keyup', this.filterValidation, this);
8085         }
8086         else if(this.validationEvent !== false){
8087             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8088         }
8089         
8090         if(this.selectOnFocus){
8091             this.on("focus", this.preFocus, this);
8092             
8093         }
8094         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8095             this.inputEl().on("keypress", this.filterKeys, this);
8096         }
8097        /* if(this.grow){
8098             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8099             this.el.on("click", this.autoSize,  this);
8100         }
8101         */
8102         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8103             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8104         }
8105         
8106         if (typeof(this.before) == 'object') {
8107             this.before.render(this.el.select('.roo-input-before',true).first());
8108         }
8109         if (typeof(this.after) == 'object') {
8110             this.after.render(this.el.select('.roo-input-after',true).first());
8111         }
8112         
8113         
8114     },
8115     filterValidation : function(e){
8116         if(!e.isNavKeyPress()){
8117             this.validationTask.delay(this.validationDelay);
8118         }
8119     },
8120      /**
8121      * Validates the field value
8122      * @return {Boolean} True if the value is valid, else false
8123      */
8124     validate : function(){
8125         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8126         if(this.disabled || this.validateValue(this.getRawValue())){
8127             this.markValid();
8128             return true;
8129         }
8130         
8131         this.markInvalid();
8132         return false;
8133     },
8134     
8135     
8136     /**
8137      * Validates a value according to the field's validation rules and marks the field as invalid
8138      * if the validation fails
8139      * @param {Mixed} value The value to validate
8140      * @return {Boolean} True if the value is valid, else false
8141      */
8142     validateValue : function(value){
8143         if(value.length < 1)  { // if it's blank
8144             if(this.allowBlank){
8145                 return true;
8146             }
8147             return false;
8148         }
8149         
8150         if(value.length < this.minLength){
8151             return false;
8152         }
8153         if(value.length > this.maxLength){
8154             return false;
8155         }
8156         if(this.vtype){
8157             var vt = Roo.form.VTypes;
8158             if(!vt[this.vtype](value, this)){
8159                 return false;
8160             }
8161         }
8162         if(typeof this.validator == "function"){
8163             var msg = this.validator(value);
8164             if(msg !== true){
8165                 return false;
8166             }
8167         }
8168         
8169         if(this.regex && !this.regex.test(value)){
8170             return false;
8171         }
8172         
8173         return true;
8174     },
8175
8176     
8177     
8178      // private
8179     fireKey : function(e){
8180         //Roo.log('field ' + e.getKey());
8181         if(e.isNavKeyPress()){
8182             this.fireEvent("specialkey", this, e);
8183         }
8184     },
8185     focus : function (selectText){
8186         if(this.rendered){
8187             this.inputEl().focus();
8188             if(selectText === true){
8189                 this.inputEl().dom.select();
8190             }
8191         }
8192         return this;
8193     } ,
8194     
8195     onFocus : function(){
8196         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8197            // this.el.addClass(this.focusClass);
8198         }
8199         if(!this.hasFocus){
8200             this.hasFocus = true;
8201             this.startValue = this.getValue();
8202             this.fireEvent("focus", this);
8203         }
8204     },
8205     
8206     beforeBlur : Roo.emptyFn,
8207
8208     
8209     // private
8210     onBlur : function(){
8211         this.beforeBlur();
8212         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8213             //this.el.removeClass(this.focusClass);
8214         }
8215         this.hasFocus = false;
8216         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8217             this.validate();
8218         }
8219         var v = this.getValue();
8220         if(String(v) !== String(this.startValue)){
8221             this.fireEvent('change', this, v, this.startValue);
8222         }
8223         this.fireEvent("blur", this);
8224     },
8225     
8226     /**
8227      * Resets the current field value to the originally loaded value and clears any validation messages
8228      */
8229     reset : function(){
8230         this.setValue(this.originalValue);
8231         this.validate();
8232     },
8233      /**
8234      * Returns the name of the field
8235      * @return {Mixed} name The name field
8236      */
8237     getName: function(){
8238         return this.name;
8239     },
8240      /**
8241      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8242      * @return {Mixed} value The field value
8243      */
8244     getValue : function(){
8245         
8246         var v = this.inputEl().getValue();
8247         
8248         return v;
8249     },
8250     /**
8251      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8252      * @return {Mixed} value The field value
8253      */
8254     getRawValue : function(){
8255         var v = this.inputEl().getValue();
8256         
8257         return v;
8258     },
8259     
8260     /**
8261      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8262      * @param {Mixed} value The value to set
8263      */
8264     setRawValue : function(v){
8265         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8266     },
8267     
8268     selectText : function(start, end){
8269         var v = this.getRawValue();
8270         if(v.length > 0){
8271             start = start === undefined ? 0 : start;
8272             end = end === undefined ? v.length : end;
8273             var d = this.inputEl().dom;
8274             if(d.setSelectionRange){
8275                 d.setSelectionRange(start, end);
8276             }else if(d.createTextRange){
8277                 var range = d.createTextRange();
8278                 range.moveStart("character", start);
8279                 range.moveEnd("character", v.length-end);
8280                 range.select();
8281             }
8282         }
8283     },
8284     
8285     /**
8286      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8287      * @param {Mixed} value The value to set
8288      */
8289     setValue : function(v){
8290         this.value = v;
8291         if(this.rendered){
8292             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8293             this.validate();
8294         }
8295     },
8296     
8297     /*
8298     processValue : function(value){
8299         if(this.stripCharsRe){
8300             var newValue = value.replace(this.stripCharsRe, '');
8301             if(newValue !== value){
8302                 this.setRawValue(newValue);
8303                 return newValue;
8304             }
8305         }
8306         return value;
8307     },
8308   */
8309     preFocus : function(){
8310         
8311         if(this.selectOnFocus){
8312             this.inputEl().dom.select();
8313         }
8314     },
8315     filterKeys : function(e){
8316         var k = e.getKey();
8317         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8318             return;
8319         }
8320         var c = e.getCharCode(), cc = String.fromCharCode(c);
8321         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8322             return;
8323         }
8324         if(!this.maskRe.test(cc)){
8325             e.stopEvent();
8326         }
8327     },
8328      /**
8329      * Clear any invalid styles/messages for this field
8330      */
8331     clearInvalid : function(){
8332         
8333         if(!this.el || this.preventMark){ // not rendered
8334             return;
8335         }
8336         this.el.removeClass(this.invalidClass);
8337         
8338         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8339             
8340             var feedback = this.el.select('.form-control-feedback', true).first();
8341             
8342             if(feedback){
8343                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8344             }
8345             
8346         }
8347         
8348         this.fireEvent('valid', this);
8349     },
8350     
8351      /**
8352      * Mark this field as valid
8353      */
8354     markValid : function()
8355     {
8356         if(!this.el  || this.preventMark){ // not rendered
8357             return;
8358         }
8359         
8360         this.el.removeClass([this.invalidClass, this.validClass]);
8361         
8362         var feedback = this.el.select('.form-control-feedback', true).first();
8363             
8364         if(feedback){
8365             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8366         }
8367
8368         if(this.disabled || this.allowBlank){
8369             return;
8370         }
8371         
8372         var formGroup = this.el.findParent('.form-group', false, true);
8373         
8374         if(formGroup){
8375             
8376             var label = formGroup.select('label', true).first();
8377             var icon = formGroup.select('i.fa-star', true).first();
8378             
8379             if(label && icon){
8380                 icon.remove();
8381             }
8382         }
8383         
8384         this.el.addClass(this.validClass);
8385         
8386         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8387             
8388             var feedback = this.el.select('.form-control-feedback', true).first();
8389             
8390             if(feedback){
8391                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8392                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8393             }
8394             
8395         }
8396         
8397         this.fireEvent('valid', this);
8398     },
8399     
8400      /**
8401      * Mark this field as invalid
8402      * @param {String} msg The validation message
8403      */
8404     markInvalid : function(msg)
8405     {
8406         if(!this.el  || this.preventMark){ // not rendered
8407             return;
8408         }
8409         
8410         this.el.removeClass([this.invalidClass, this.validClass]);
8411         
8412         var feedback = this.el.select('.form-control-feedback', true).first();
8413             
8414         if(feedback){
8415             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8416         }
8417
8418         if(this.disabled || this.allowBlank){
8419             return;
8420         }
8421         
8422         var formGroup = this.el.findParent('.form-group', false, true);
8423         
8424         if(formGroup){
8425             var label = formGroup.select('label', true).first();
8426             var icon = formGroup.select('i.fa-star', true).first();
8427
8428             if(!this.getValue().length && label && !icon){
8429                 this.el.findParent('.form-group', false, true).createChild({
8430                     tag : 'i',
8431                     cls : 'text-danger fa fa-lg fa-star',
8432                     tooltip : 'This field is required',
8433                     style : 'margin-right:5px;'
8434                 }, label, true);
8435             }
8436         }
8437         
8438         
8439         this.el.addClass(this.invalidClass);
8440         
8441         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8442             
8443             var feedback = this.el.select('.form-control-feedback', true).first();
8444             
8445             if(feedback){
8446                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8447                 
8448                 if(this.getValue().length || this.forceFeedback){
8449                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8450                 }
8451                 
8452             }
8453             
8454         }
8455         
8456         this.fireEvent('invalid', this, msg);
8457     },
8458     // private
8459     SafariOnKeyDown : function(event)
8460     {
8461         // this is a workaround for a password hang bug on chrome/ webkit.
8462         
8463         var isSelectAll = false;
8464         
8465         if(this.inputEl().dom.selectionEnd > 0){
8466             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8467         }
8468         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8469             event.preventDefault();
8470             this.setValue('');
8471             return;
8472         }
8473         
8474         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8475             
8476             event.preventDefault();
8477             // this is very hacky as keydown always get's upper case.
8478             //
8479             var cc = String.fromCharCode(event.getCharCode());
8480             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8481             
8482         }
8483     },
8484     adjustWidth : function(tag, w){
8485         tag = tag.toLowerCase();
8486         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8487             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8488                 if(tag == 'input'){
8489                     return w + 2;
8490                 }
8491                 if(tag == 'textarea'){
8492                     return w-2;
8493                 }
8494             }else if(Roo.isOpera){
8495                 if(tag == 'input'){
8496                     return w + 2;
8497                 }
8498                 if(tag == 'textarea'){
8499                     return w-2;
8500                 }
8501             }
8502         }
8503         return w;
8504     }
8505     
8506 });
8507
8508  
8509 /*
8510  * - LGPL
8511  *
8512  * Input
8513  * 
8514  */
8515
8516 /**
8517  * @class Roo.bootstrap.TextArea
8518  * @extends Roo.bootstrap.Input
8519  * Bootstrap TextArea class
8520  * @cfg {Number} cols Specifies the visible width of a text area
8521  * @cfg {Number} rows Specifies the visible number of lines in a text area
8522  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8523  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8524  * @cfg {string} html text
8525  * 
8526  * @constructor
8527  * Create a new TextArea
8528  * @param {Object} config The config object
8529  */
8530
8531 Roo.bootstrap.TextArea = function(config){
8532     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8533    
8534 };
8535
8536 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8537      
8538     cols : false,
8539     rows : 5,
8540     readOnly : false,
8541     warp : 'soft',
8542     resize : false,
8543     value: false,
8544     html: false,
8545     
8546     getAutoCreate : function(){
8547         
8548         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8549         
8550         var id = Roo.id();
8551         
8552         var cfg = {};
8553         
8554         var input =  {
8555             tag: 'textarea',
8556             id : id,
8557             warp : this.warp,
8558             rows : this.rows,
8559             value : this.value || '',
8560             html: this.html || '',
8561             cls : 'form-control',
8562             placeholder : this.placeholder || '' 
8563             
8564         };
8565         
8566         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8567             input.maxLength = this.maxLength;
8568         }
8569         
8570         if(this.resize){
8571             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8572         }
8573         
8574         if(this.cols){
8575             input.cols = this.cols;
8576         }
8577         
8578         if (this.readOnly) {
8579             input.readonly = true;
8580         }
8581         
8582         if (this.name) {
8583             input.name = this.name;
8584         }
8585         
8586         if (this.size) {
8587             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8588         }
8589         
8590         var settings=this;
8591         ['xs','sm','md','lg'].map(function(size){
8592             if (settings[size]) {
8593                 cfg.cls += ' col-' + size + '-' + settings[size];
8594             }
8595         });
8596         
8597         var inputblock = input;
8598         
8599         if(this.hasFeedback && !this.allowBlank){
8600             
8601             var feedback = {
8602                 tag: 'span',
8603                 cls: 'glyphicon form-control-feedback'
8604             };
8605
8606             inputblock = {
8607                 cls : 'has-feedback',
8608                 cn :  [
8609                     input,
8610                     feedback
8611                 ] 
8612             };  
8613         }
8614         
8615         
8616         if (this.before || this.after) {
8617             
8618             inputblock = {
8619                 cls : 'input-group',
8620                 cn :  [] 
8621             };
8622             if (this.before) {
8623                 inputblock.cn.push({
8624                     tag :'span',
8625                     cls : 'input-group-addon',
8626                     html : this.before
8627                 });
8628             }
8629             
8630             inputblock.cn.push(input);
8631             
8632             if(this.hasFeedback && !this.allowBlank){
8633                 inputblock.cls += ' has-feedback';
8634                 inputblock.cn.push(feedback);
8635             }
8636             
8637             if (this.after) {
8638                 inputblock.cn.push({
8639                     tag :'span',
8640                     cls : 'input-group-addon',
8641                     html : this.after
8642                 });
8643             }
8644             
8645         }
8646         
8647         if (align ==='left' && this.fieldLabel.length) {
8648                 Roo.log("left and has label");
8649                 cfg.cn = [
8650                     
8651                     {
8652                         tag: 'label',
8653                         'for' :  id,
8654                         cls : 'control-label col-sm-' + this.labelWidth,
8655                         html : this.fieldLabel
8656                         
8657                     },
8658                     {
8659                         cls : "col-sm-" + (12 - this.labelWidth), 
8660                         cn: [
8661                             inputblock
8662                         ]
8663                     }
8664                     
8665                 ];
8666         } else if ( this.fieldLabel.length) {
8667                 Roo.log(" label");
8668                  cfg.cn = [
8669                    
8670                     {
8671                         tag: 'label',
8672                         //cls : 'input-group-addon',
8673                         html : this.fieldLabel
8674                         
8675                     },
8676                     
8677                     inputblock
8678                     
8679                 ];
8680
8681         } else {
8682             
8683                    Roo.log(" no label && no align");
8684                 cfg.cn = [
8685                     
8686                         inputblock
8687                     
8688                 ];
8689                 
8690                 
8691         }
8692         
8693         if (this.disabled) {
8694             input.disabled=true;
8695         }
8696         
8697         return cfg;
8698         
8699     },
8700     /**
8701      * return the real textarea element.
8702      */
8703     inputEl: function ()
8704     {
8705         return this.el.select('textarea.form-control',true).first();
8706     }
8707 });
8708
8709  
8710 /*
8711  * - LGPL
8712  *
8713  * trigger field - base class for combo..
8714  * 
8715  */
8716  
8717 /**
8718  * @class Roo.bootstrap.TriggerField
8719  * @extends Roo.bootstrap.Input
8720  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8721  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8722  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8723  * for which you can provide a custom implementation.  For example:
8724  * <pre><code>
8725 var trigger = new Roo.bootstrap.TriggerField();
8726 trigger.onTriggerClick = myTriggerFn;
8727 trigger.applyTo('my-field');
8728 </code></pre>
8729  *
8730  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8731  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8732  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8733  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8734  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8735
8736  * @constructor
8737  * Create a new TriggerField.
8738  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8739  * to the base TextField)
8740  */
8741 Roo.bootstrap.TriggerField = function(config){
8742     this.mimicing = false;
8743     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8744 };
8745
8746 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8747     /**
8748      * @cfg {String} triggerClass A CSS class to apply to the trigger
8749      */
8750      /**
8751      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8752      */
8753     hideTrigger:false,
8754
8755     /**
8756      * @cfg {Boolean} removable (true|false) special filter default false
8757      */
8758     removable : false,
8759     
8760     /** @cfg {Boolean} grow @hide */
8761     /** @cfg {Number} growMin @hide */
8762     /** @cfg {Number} growMax @hide */
8763
8764     /**
8765      * @hide 
8766      * @method
8767      */
8768     autoSize: Roo.emptyFn,
8769     // private
8770     monitorTab : true,
8771     // private
8772     deferHeight : true,
8773
8774     
8775     actionMode : 'wrap',
8776     
8777     caret : false,
8778     
8779     
8780     getAutoCreate : function(){
8781        
8782         var align = this.labelAlign || this.parentLabelAlign();
8783         
8784         var id = Roo.id();
8785         
8786         var cfg = {
8787             cls: 'form-group' //input-group
8788         };
8789         
8790         
8791         var input =  {
8792             tag: 'input',
8793             id : id,
8794             type : this.inputType,
8795             cls : 'form-control',
8796             autocomplete: 'new-password',
8797             placeholder : this.placeholder || '' 
8798             
8799         };
8800         if (this.name) {
8801             input.name = this.name;
8802         }
8803         if (this.size) {
8804             input.cls += ' input-' + this.size;
8805         }
8806         
8807         if (this.disabled) {
8808             input.disabled=true;
8809         }
8810         
8811         var inputblock = input;
8812         
8813         if(this.hasFeedback && !this.allowBlank){
8814             
8815             var feedback = {
8816                 tag: 'span',
8817                 cls: 'glyphicon form-control-feedback'
8818             };
8819             
8820             if(this.removable && !this.editable && !this.tickable){
8821                 inputblock = {
8822                     cls : 'has-feedback',
8823                     cn :  [
8824                         inputblock,
8825                         {
8826                             tag: 'button',
8827                             html : 'x',
8828                             cls : 'roo-combo-removable-btn close'
8829                         },
8830                         feedback
8831                     ] 
8832                 };
8833             } else {
8834                 inputblock = {
8835                     cls : 'has-feedback',
8836                     cn :  [
8837                         inputblock,
8838                         feedback
8839                     ] 
8840                 };
8841             }
8842
8843         } else {
8844             if(this.removable && !this.editable && !this.tickable){
8845                 inputblock = {
8846                     cls : 'roo-removable',
8847                     cn :  [
8848                         inputblock,
8849                         {
8850                             tag: 'button',
8851                             html : 'x',
8852                             cls : 'roo-combo-removable-btn close'
8853                         }
8854                     ] 
8855                 };
8856             }
8857         }
8858         
8859         if (this.before || this.after) {
8860             
8861             inputblock = {
8862                 cls : 'input-group',
8863                 cn :  [] 
8864             };
8865             if (this.before) {
8866                 inputblock.cn.push({
8867                     tag :'span',
8868                     cls : 'input-group-addon',
8869                     html : this.before
8870                 });
8871             }
8872             
8873             inputblock.cn.push(input);
8874             
8875             if(this.hasFeedback && !this.allowBlank){
8876                 inputblock.cls += ' has-feedback';
8877                 inputblock.cn.push(feedback);
8878             }
8879             
8880             if (this.after) {
8881                 inputblock.cn.push({
8882                     tag :'span',
8883                     cls : 'input-group-addon',
8884                     html : this.after
8885                 });
8886             }
8887             
8888         };
8889         
8890         var box = {
8891             tag: 'div',
8892             cn: [
8893                 {
8894                     tag: 'input',
8895                     type : 'hidden',
8896                     cls: 'form-hidden-field'
8897                 },
8898                 inputblock
8899             ]
8900             
8901         };
8902         
8903         if(this.multiple){
8904             Roo.log('multiple');
8905             
8906             box = {
8907                 tag: 'div',
8908                 cn: [
8909                     {
8910                         tag: 'input',
8911                         type : 'hidden',
8912                         cls: 'form-hidden-field'
8913                     },
8914                     {
8915                         tag: 'ul',
8916                         cls: 'select2-choices',
8917                         cn:[
8918                             {
8919                                 tag: 'li',
8920                                 cls: 'select2-search-field',
8921                                 cn: [
8922
8923                                     inputblock
8924                                 ]
8925                             }
8926                         ]
8927                     }
8928                 ]
8929             }
8930         };
8931         
8932         var combobox = {
8933             cls: 'select2-container input-group',
8934             cn: [
8935                 box
8936 //                {
8937 //                    tag: 'ul',
8938 //                    cls: 'typeahead typeahead-long dropdown-menu',
8939 //                    style: 'display:none'
8940 //                }
8941             ]
8942         };
8943         
8944         if(!this.multiple && this.showToggleBtn){
8945             
8946             var caret = {
8947                         tag: 'span',
8948                         cls: 'caret'
8949              };
8950             if (this.caret != false) {
8951                 caret = {
8952                      tag: 'i',
8953                      cls: 'fa fa-' + this.caret
8954                 };
8955                 
8956             }
8957             
8958             combobox.cn.push({
8959                 tag :'span',
8960                 cls : 'input-group-addon btn dropdown-toggle',
8961                 cn : [
8962                     caret,
8963                     {
8964                         tag: 'span',
8965                         cls: 'combobox-clear',
8966                         cn  : [
8967                             {
8968                                 tag : 'i',
8969                                 cls: 'icon-remove'
8970                             }
8971                         ]
8972                     }
8973                 ]
8974
8975             })
8976         }
8977         
8978         if(this.multiple){
8979             combobox.cls += ' select2-container-multi';
8980         }
8981         
8982         if (align ==='left' && this.fieldLabel.length) {
8983             
8984                 Roo.log("left and has label");
8985                 cfg.cn = [
8986                     
8987                     {
8988                         tag: 'label',
8989                         'for' :  id,
8990                         cls : 'control-label col-sm-' + this.labelWidth,
8991                         html : this.fieldLabel
8992                         
8993                     },
8994                     {
8995                         cls : "col-sm-" + (12 - this.labelWidth), 
8996                         cn: [
8997                             combobox
8998                         ]
8999                     }
9000                     
9001                 ];
9002         } else if ( this.fieldLabel.length) {
9003                 Roo.log(" label");
9004                  cfg.cn = [
9005                    
9006                     {
9007                         tag: 'label',
9008                         //cls : 'input-group-addon',
9009                         html : this.fieldLabel
9010                         
9011                     },
9012                     
9013                     combobox
9014                     
9015                 ];
9016
9017         } else {
9018             
9019                 Roo.log(" no label && no align");
9020                 cfg = combobox
9021                      
9022                 
9023         }
9024          
9025         var settings=this;
9026         ['xs','sm','md','lg'].map(function(size){
9027             if (settings[size]) {
9028                 cfg.cls += ' col-' + size + '-' + settings[size];
9029             }
9030         });
9031         Roo.log(cfg);
9032         return cfg;
9033         
9034     },
9035     
9036     
9037     
9038     // private
9039     onResize : function(w, h){
9040 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9041 //        if(typeof w == 'number'){
9042 //            var x = w - this.trigger.getWidth();
9043 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9044 //            this.trigger.setStyle('left', x+'px');
9045 //        }
9046     },
9047
9048     // private
9049     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9050
9051     // private
9052     getResizeEl : function(){
9053         return this.inputEl();
9054     },
9055
9056     // private
9057     getPositionEl : function(){
9058         return this.inputEl();
9059     },
9060
9061     // private
9062     alignErrorIcon : function(){
9063         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9064     },
9065
9066     // private
9067     initEvents : function(){
9068         
9069         this.createList();
9070         
9071         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9072         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9073         if(!this.multiple && this.showToggleBtn){
9074             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9075             if(this.hideTrigger){
9076                 this.trigger.setDisplayed(false);
9077             }
9078             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9079         }
9080         
9081         if(this.multiple){
9082             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9083         }
9084         
9085         if(this.removable && !this.editable && !this.tickable){
9086             var close = this.closeTriggerEl();
9087             
9088             if(close){
9089                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9090                 close.on('click', this.removeBtnClick, this, close);
9091             }
9092         }
9093         
9094         //this.trigger.addClassOnOver('x-form-trigger-over');
9095         //this.trigger.addClassOnClick('x-form-trigger-click');
9096         
9097         //if(!this.width){
9098         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9099         //}
9100     },
9101     
9102     closeTriggerEl : function()
9103     {
9104         var close = this.el.select('.roo-combo-removable-btn', true).first();
9105         return close ? close : false;
9106     },
9107     
9108     removeBtnClick : function(e, h, el)
9109     {
9110         e.preventDefault();
9111         
9112         if(this.fireEvent("remove", this) !== false){
9113             this.reset();
9114         }
9115     },
9116     
9117     createList : function()
9118     {
9119         this.list = Roo.get(document.body).createChild({
9120             tag: 'ul',
9121             cls: 'typeahead typeahead-long dropdown-menu',
9122             style: 'display:none'
9123         });
9124         
9125         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9126         
9127     },
9128
9129     // private
9130     initTrigger : function(){
9131        
9132     },
9133
9134     // private
9135     onDestroy : function(){
9136         if(this.trigger){
9137             this.trigger.removeAllListeners();
9138           //  this.trigger.remove();
9139         }
9140         //if(this.wrap){
9141         //    this.wrap.remove();
9142         //}
9143         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9144     },
9145
9146     // private
9147     onFocus : function(){
9148         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9149         /*
9150         if(!this.mimicing){
9151             this.wrap.addClass('x-trigger-wrap-focus');
9152             this.mimicing = true;
9153             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9154             if(this.monitorTab){
9155                 this.el.on("keydown", this.checkTab, this);
9156             }
9157         }
9158         */
9159     },
9160
9161     // private
9162     checkTab : function(e){
9163         if(e.getKey() == e.TAB){
9164             this.triggerBlur();
9165         }
9166     },
9167
9168     // private
9169     onBlur : function(){
9170         // do nothing
9171     },
9172
9173     // private
9174     mimicBlur : function(e, t){
9175         /*
9176         if(!this.wrap.contains(t) && this.validateBlur()){
9177             this.triggerBlur();
9178         }
9179         */
9180     },
9181
9182     // private
9183     triggerBlur : function(){
9184         this.mimicing = false;
9185         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9186         if(this.monitorTab){
9187             this.el.un("keydown", this.checkTab, this);
9188         }
9189         //this.wrap.removeClass('x-trigger-wrap-focus');
9190         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9191     },
9192
9193     // private
9194     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9195     validateBlur : function(e, t){
9196         return true;
9197     },
9198
9199     // private
9200     onDisable : function(){
9201         this.inputEl().dom.disabled = true;
9202         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9203         //if(this.wrap){
9204         //    this.wrap.addClass('x-item-disabled');
9205         //}
9206     },
9207
9208     // private
9209     onEnable : function(){
9210         this.inputEl().dom.disabled = false;
9211         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9212         //if(this.wrap){
9213         //    this.el.removeClass('x-item-disabled');
9214         //}
9215     },
9216
9217     // private
9218     onShow : function(){
9219         var ae = this.getActionEl();
9220         
9221         if(ae){
9222             ae.dom.style.display = '';
9223             ae.dom.style.visibility = 'visible';
9224         }
9225     },
9226
9227     // private
9228     
9229     onHide : function(){
9230         var ae = this.getActionEl();
9231         ae.dom.style.display = 'none';
9232     },
9233
9234     /**
9235      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9236      * by an implementing function.
9237      * @method
9238      * @param {EventObject} e
9239      */
9240     onTriggerClick : Roo.emptyFn
9241 });
9242  /*
9243  * Based on:
9244  * Ext JS Library 1.1.1
9245  * Copyright(c) 2006-2007, Ext JS, LLC.
9246  *
9247  * Originally Released Under LGPL - original licence link has changed is not relivant.
9248  *
9249  * Fork - LGPL
9250  * <script type="text/javascript">
9251  */
9252
9253
9254 /**
9255  * @class Roo.data.SortTypes
9256  * @singleton
9257  * Defines the default sorting (casting?) comparison functions used when sorting data.
9258  */
9259 Roo.data.SortTypes = {
9260     /**
9261      * Default sort that does nothing
9262      * @param {Mixed} s The value being converted
9263      * @return {Mixed} The comparison value
9264      */
9265     none : function(s){
9266         return s;
9267     },
9268     
9269     /**
9270      * The regular expression used to strip tags
9271      * @type {RegExp}
9272      * @property
9273      */
9274     stripTagsRE : /<\/?[^>]+>/gi,
9275     
9276     /**
9277      * Strips all HTML tags to sort on text only
9278      * @param {Mixed} s The value being converted
9279      * @return {String} The comparison value
9280      */
9281     asText : function(s){
9282         return String(s).replace(this.stripTagsRE, "");
9283     },
9284     
9285     /**
9286      * Strips all HTML tags to sort on text only - Case insensitive
9287      * @param {Mixed} s The value being converted
9288      * @return {String} The comparison value
9289      */
9290     asUCText : function(s){
9291         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9292     },
9293     
9294     /**
9295      * Case insensitive string
9296      * @param {Mixed} s The value being converted
9297      * @return {String} The comparison value
9298      */
9299     asUCString : function(s) {
9300         return String(s).toUpperCase();
9301     },
9302     
9303     /**
9304      * Date sorting
9305      * @param {Mixed} s The value being converted
9306      * @return {Number} The comparison value
9307      */
9308     asDate : function(s) {
9309         if(!s){
9310             return 0;
9311         }
9312         if(s instanceof Date){
9313             return s.getTime();
9314         }
9315         return Date.parse(String(s));
9316     },
9317     
9318     /**
9319      * Float sorting
9320      * @param {Mixed} s The value being converted
9321      * @return {Float} The comparison value
9322      */
9323     asFloat : function(s) {
9324         var val = parseFloat(String(s).replace(/,/g, ""));
9325         if(isNaN(val)) val = 0;
9326         return val;
9327     },
9328     
9329     /**
9330      * Integer sorting
9331      * @param {Mixed} s The value being converted
9332      * @return {Number} The comparison value
9333      */
9334     asInt : function(s) {
9335         var val = parseInt(String(s).replace(/,/g, ""));
9336         if(isNaN(val)) val = 0;
9337         return val;
9338     }
9339 };/*
9340  * Based on:
9341  * Ext JS Library 1.1.1
9342  * Copyright(c) 2006-2007, Ext JS, LLC.
9343  *
9344  * Originally Released Under LGPL - original licence link has changed is not relivant.
9345  *
9346  * Fork - LGPL
9347  * <script type="text/javascript">
9348  */
9349
9350 /**
9351 * @class Roo.data.Record
9352  * Instances of this class encapsulate both record <em>definition</em> information, and record
9353  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9354  * to access Records cached in an {@link Roo.data.Store} object.<br>
9355  * <p>
9356  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9357  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9358  * objects.<br>
9359  * <p>
9360  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9361  * @constructor
9362  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9363  * {@link #create}. The parameters are the same.
9364  * @param {Array} data An associative Array of data values keyed by the field name.
9365  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9366  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9367  * not specified an integer id is generated.
9368  */
9369 Roo.data.Record = function(data, id){
9370     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9371     this.data = data;
9372 };
9373
9374 /**
9375  * Generate a constructor for a specific record layout.
9376  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9377  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9378  * Each field definition object may contain the following properties: <ul>
9379  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9380  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9381  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9382  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9383  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9384  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9385  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9386  * this may be omitted.</p></li>
9387  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9388  * <ul><li>auto (Default, implies no conversion)</li>
9389  * <li>string</li>
9390  * <li>int</li>
9391  * <li>float</li>
9392  * <li>boolean</li>
9393  * <li>date</li></ul></p></li>
9394  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9395  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9396  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9397  * by the Reader into an object that will be stored in the Record. It is passed the
9398  * following parameters:<ul>
9399  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9400  * </ul></p></li>
9401  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9402  * </ul>
9403  * <br>usage:<br><pre><code>
9404 var TopicRecord = Roo.data.Record.create(
9405     {name: 'title', mapping: 'topic_title'},
9406     {name: 'author', mapping: 'username'},
9407     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9408     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9409     {name: 'lastPoster', mapping: 'user2'},
9410     {name: 'excerpt', mapping: 'post_text'}
9411 );
9412
9413 var myNewRecord = new TopicRecord({
9414     title: 'Do my job please',
9415     author: 'noobie',
9416     totalPosts: 1,
9417     lastPost: new Date(),
9418     lastPoster: 'Animal',
9419     excerpt: 'No way dude!'
9420 });
9421 myStore.add(myNewRecord);
9422 </code></pre>
9423  * @method create
9424  * @static
9425  */
9426 Roo.data.Record.create = function(o){
9427     var f = function(){
9428         f.superclass.constructor.apply(this, arguments);
9429     };
9430     Roo.extend(f, Roo.data.Record);
9431     var p = f.prototype;
9432     p.fields = new Roo.util.MixedCollection(false, function(field){
9433         return field.name;
9434     });
9435     for(var i = 0, len = o.length; i < len; i++){
9436         p.fields.add(new Roo.data.Field(o[i]));
9437     }
9438     f.getField = function(name){
9439         return p.fields.get(name);  
9440     };
9441     return f;
9442 };
9443
9444 Roo.data.Record.AUTO_ID = 1000;
9445 Roo.data.Record.EDIT = 'edit';
9446 Roo.data.Record.REJECT = 'reject';
9447 Roo.data.Record.COMMIT = 'commit';
9448
9449 Roo.data.Record.prototype = {
9450     /**
9451      * Readonly flag - true if this record has been modified.
9452      * @type Boolean
9453      */
9454     dirty : false,
9455     editing : false,
9456     error: null,
9457     modified: null,
9458
9459     // private
9460     join : function(store){
9461         this.store = store;
9462     },
9463
9464     /**
9465      * Set the named field to the specified value.
9466      * @param {String} name The name of the field to set.
9467      * @param {Object} value The value to set the field to.
9468      */
9469     set : function(name, value){
9470         if(this.data[name] == value){
9471             return;
9472         }
9473         this.dirty = true;
9474         if(!this.modified){
9475             this.modified = {};
9476         }
9477         if(typeof this.modified[name] == 'undefined'){
9478             this.modified[name] = this.data[name];
9479         }
9480         this.data[name] = value;
9481         if(!this.editing && this.store){
9482             this.store.afterEdit(this);
9483         }       
9484     },
9485
9486     /**
9487      * Get the value of the named field.
9488      * @param {String} name The name of the field to get the value of.
9489      * @return {Object} The value of the field.
9490      */
9491     get : function(name){
9492         return this.data[name]; 
9493     },
9494
9495     // private
9496     beginEdit : function(){
9497         this.editing = true;
9498         this.modified = {}; 
9499     },
9500
9501     // private
9502     cancelEdit : function(){
9503         this.editing = false;
9504         delete this.modified;
9505     },
9506
9507     // private
9508     endEdit : function(){
9509         this.editing = false;
9510         if(this.dirty && this.store){
9511             this.store.afterEdit(this);
9512         }
9513     },
9514
9515     /**
9516      * Usually called by the {@link Roo.data.Store} which owns the Record.
9517      * Rejects all changes made to the Record since either creation, or the last commit operation.
9518      * Modified fields are reverted to their original values.
9519      * <p>
9520      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9521      * of reject operations.
9522      */
9523     reject : function(){
9524         var m = this.modified;
9525         for(var n in m){
9526             if(typeof m[n] != "function"){
9527                 this.data[n] = m[n];
9528             }
9529         }
9530         this.dirty = false;
9531         delete this.modified;
9532         this.editing = false;
9533         if(this.store){
9534             this.store.afterReject(this);
9535         }
9536     },
9537
9538     /**
9539      * Usually called by the {@link Roo.data.Store} which owns the Record.
9540      * Commits all changes made to the Record since either creation, or the last commit operation.
9541      * <p>
9542      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9543      * of commit operations.
9544      */
9545     commit : function(){
9546         this.dirty = false;
9547         delete this.modified;
9548         this.editing = false;
9549         if(this.store){
9550             this.store.afterCommit(this);
9551         }
9552     },
9553
9554     // private
9555     hasError : function(){
9556         return this.error != null;
9557     },
9558
9559     // private
9560     clearError : function(){
9561         this.error = null;
9562     },
9563
9564     /**
9565      * Creates a copy of this record.
9566      * @param {String} id (optional) A new record id if you don't want to use this record's id
9567      * @return {Record}
9568      */
9569     copy : function(newId) {
9570         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9571     }
9572 };/*
9573  * Based on:
9574  * Ext JS Library 1.1.1
9575  * Copyright(c) 2006-2007, Ext JS, LLC.
9576  *
9577  * Originally Released Under LGPL - original licence link has changed is not relivant.
9578  *
9579  * Fork - LGPL
9580  * <script type="text/javascript">
9581  */
9582
9583
9584
9585 /**
9586  * @class Roo.data.Store
9587  * @extends Roo.util.Observable
9588  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9589  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9590  * <p>
9591  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
9592  * has no knowledge of the format of the data returned by the Proxy.<br>
9593  * <p>
9594  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9595  * instances from the data object. These records are cached and made available through accessor functions.
9596  * @constructor
9597  * Creates a new Store.
9598  * @param {Object} config A config object containing the objects needed for the Store to access data,
9599  * and read the data into Records.
9600  */
9601 Roo.data.Store = function(config){
9602     this.data = new Roo.util.MixedCollection(false);
9603     this.data.getKey = function(o){
9604         return o.id;
9605     };
9606     this.baseParams = {};
9607     // private
9608     this.paramNames = {
9609         "start" : "start",
9610         "limit" : "limit",
9611         "sort" : "sort",
9612         "dir" : "dir",
9613         "multisort" : "_multisort"
9614     };
9615
9616     if(config && config.data){
9617         this.inlineData = config.data;
9618         delete config.data;
9619     }
9620
9621     Roo.apply(this, config);
9622     
9623     if(this.reader){ // reader passed
9624         this.reader = Roo.factory(this.reader, Roo.data);
9625         this.reader.xmodule = this.xmodule || false;
9626         if(!this.recordType){
9627             this.recordType = this.reader.recordType;
9628         }
9629         if(this.reader.onMetaChange){
9630             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9631         }
9632     }
9633
9634     if(this.recordType){
9635         this.fields = this.recordType.prototype.fields;
9636     }
9637     this.modified = [];
9638
9639     this.addEvents({
9640         /**
9641          * @event datachanged
9642          * Fires when the data cache has changed, and a widget which is using this Store
9643          * as a Record cache should refresh its view.
9644          * @param {Store} this
9645          */
9646         datachanged : true,
9647         /**
9648          * @event metachange
9649          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9650          * @param {Store} this
9651          * @param {Object} meta The JSON metadata
9652          */
9653         metachange : true,
9654         /**
9655          * @event add
9656          * Fires when Records have been added to the Store
9657          * @param {Store} this
9658          * @param {Roo.data.Record[]} records The array of Records added
9659          * @param {Number} index The index at which the record(s) were added
9660          */
9661         add : true,
9662         /**
9663          * @event remove
9664          * Fires when a Record has been removed from the Store
9665          * @param {Store} this
9666          * @param {Roo.data.Record} record The Record that was removed
9667          * @param {Number} index The index at which the record was removed
9668          */
9669         remove : true,
9670         /**
9671          * @event update
9672          * Fires when a Record has been updated
9673          * @param {Store} this
9674          * @param {Roo.data.Record} record The Record that was updated
9675          * @param {String} operation The update operation being performed.  Value may be one of:
9676          * <pre><code>
9677  Roo.data.Record.EDIT
9678  Roo.data.Record.REJECT
9679  Roo.data.Record.COMMIT
9680          * </code></pre>
9681          */
9682         update : true,
9683         /**
9684          * @event clear
9685          * Fires when the data cache has been cleared.
9686          * @param {Store} this
9687          */
9688         clear : true,
9689         /**
9690          * @event beforeload
9691          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9692          * the load action will be canceled.
9693          * @param {Store} this
9694          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9695          */
9696         beforeload : true,
9697         /**
9698          * @event beforeloadadd
9699          * Fires after a new set of Records has been loaded.
9700          * @param {Store} this
9701          * @param {Roo.data.Record[]} records The Records that were loaded
9702          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9703          */
9704         beforeloadadd : true,
9705         /**
9706          * @event load
9707          * Fires after a new set of Records has been loaded, before they are added to the store.
9708          * @param {Store} this
9709          * @param {Roo.data.Record[]} records The Records that were loaded
9710          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9711          * @params {Object} return from reader
9712          */
9713         load : true,
9714         /**
9715          * @event loadexception
9716          * Fires if an exception occurs in the Proxy during loading.
9717          * Called with the signature of the Proxy's "loadexception" event.
9718          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9719          * 
9720          * @param {Proxy} 
9721          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9722          * @param {Object} load options 
9723          * @param {Object} jsonData from your request (normally this contains the Exception)
9724          */
9725         loadexception : true
9726     });
9727     
9728     if(this.proxy){
9729         this.proxy = Roo.factory(this.proxy, Roo.data);
9730         this.proxy.xmodule = this.xmodule || false;
9731         this.relayEvents(this.proxy,  ["loadexception"]);
9732     }
9733     this.sortToggle = {};
9734     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9735
9736     Roo.data.Store.superclass.constructor.call(this);
9737
9738     if(this.inlineData){
9739         this.loadData(this.inlineData);
9740         delete this.inlineData;
9741     }
9742 };
9743
9744 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9745      /**
9746     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9747     * without a remote query - used by combo/forms at present.
9748     */
9749     
9750     /**
9751     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9752     */
9753     /**
9754     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9755     */
9756     /**
9757     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9758     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9759     */
9760     /**
9761     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9762     * on any HTTP request
9763     */
9764     /**
9765     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9766     */
9767     /**
9768     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9769     */
9770     multiSort: false,
9771     /**
9772     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9773     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9774     */
9775     remoteSort : false,
9776
9777     /**
9778     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9779      * loaded or when a record is removed. (defaults to false).
9780     */
9781     pruneModifiedRecords : false,
9782
9783     // private
9784     lastOptions : null,
9785
9786     /**
9787      * Add Records to the Store and fires the add event.
9788      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9789      */
9790     add : function(records){
9791         records = [].concat(records);
9792         for(var i = 0, len = records.length; i < len; i++){
9793             records[i].join(this);
9794         }
9795         var index = this.data.length;
9796         this.data.addAll(records);
9797         this.fireEvent("add", this, records, index);
9798     },
9799
9800     /**
9801      * Remove a Record from the Store and fires the remove event.
9802      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9803      */
9804     remove : function(record){
9805         var index = this.data.indexOf(record);
9806         this.data.removeAt(index);
9807         if(this.pruneModifiedRecords){
9808             this.modified.remove(record);
9809         }
9810         this.fireEvent("remove", this, record, index);
9811     },
9812
9813     /**
9814      * Remove all Records from the Store and fires the clear event.
9815      */
9816     removeAll : function(){
9817         this.data.clear();
9818         if(this.pruneModifiedRecords){
9819             this.modified = [];
9820         }
9821         this.fireEvent("clear", this);
9822     },
9823
9824     /**
9825      * Inserts Records to the Store at the given index and fires the add event.
9826      * @param {Number} index The start index at which to insert the passed Records.
9827      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9828      */
9829     insert : function(index, records){
9830         records = [].concat(records);
9831         for(var i = 0, len = records.length; i < len; i++){
9832             this.data.insert(index, records[i]);
9833             records[i].join(this);
9834         }
9835         this.fireEvent("add", this, records, index);
9836     },
9837
9838     /**
9839      * Get the index within the cache of the passed Record.
9840      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9841      * @return {Number} The index of the passed Record. Returns -1 if not found.
9842      */
9843     indexOf : function(record){
9844         return this.data.indexOf(record);
9845     },
9846
9847     /**
9848      * Get the index within the cache of the Record with the passed id.
9849      * @param {String} id The id of the Record to find.
9850      * @return {Number} The index of the Record. Returns -1 if not found.
9851      */
9852     indexOfId : function(id){
9853         return this.data.indexOfKey(id);
9854     },
9855
9856     /**
9857      * Get the Record with the specified id.
9858      * @param {String} id The id of the Record to find.
9859      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9860      */
9861     getById : function(id){
9862         return this.data.key(id);
9863     },
9864
9865     /**
9866      * Get the Record at the specified index.
9867      * @param {Number} index The index of the Record to find.
9868      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9869      */
9870     getAt : function(index){
9871         return this.data.itemAt(index);
9872     },
9873
9874     /**
9875      * Returns a range of Records between specified indices.
9876      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9877      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9878      * @return {Roo.data.Record[]} An array of Records
9879      */
9880     getRange : function(start, end){
9881         return this.data.getRange(start, end);
9882     },
9883
9884     // private
9885     storeOptions : function(o){
9886         o = Roo.apply({}, o);
9887         delete o.callback;
9888         delete o.scope;
9889         this.lastOptions = o;
9890     },
9891
9892     /**
9893      * Loads the Record cache from the configured Proxy using the configured Reader.
9894      * <p>
9895      * If using remote paging, then the first load call must specify the <em>start</em>
9896      * and <em>limit</em> properties in the options.params property to establish the initial
9897      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9898      * <p>
9899      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9900      * and this call will return before the new data has been loaded. Perform any post-processing
9901      * in a callback function, or in a "load" event handler.</strong>
9902      * <p>
9903      * @param {Object} options An object containing properties which control loading options:<ul>
9904      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9905      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9906      * passed the following arguments:<ul>
9907      * <li>r : Roo.data.Record[]</li>
9908      * <li>options: Options object from the load call</li>
9909      * <li>success: Boolean success indicator</li></ul></li>
9910      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9911      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9912      * </ul>
9913      */
9914     load : function(options){
9915         options = options || {};
9916         if(this.fireEvent("beforeload", this, options) !== false){
9917             this.storeOptions(options);
9918             var p = Roo.apply(options.params || {}, this.baseParams);
9919             // if meta was not loaded from remote source.. try requesting it.
9920             if (!this.reader.metaFromRemote) {
9921                 p._requestMeta = 1;
9922             }
9923             if(this.sortInfo && this.remoteSort){
9924                 var pn = this.paramNames;
9925                 p[pn["sort"]] = this.sortInfo.field;
9926                 p[pn["dir"]] = this.sortInfo.direction;
9927             }
9928             if (this.multiSort) {
9929                 var pn = this.paramNames;
9930                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9931             }
9932             
9933             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9934         }
9935     },
9936
9937     /**
9938      * Reloads the Record cache from the configured Proxy using the configured Reader and
9939      * the options from the last load operation performed.
9940      * @param {Object} options (optional) An object containing properties which may override the options
9941      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9942      * the most recently used options are reused).
9943      */
9944     reload : function(options){
9945         this.load(Roo.applyIf(options||{}, this.lastOptions));
9946     },
9947
9948     // private
9949     // Called as a callback by the Reader during a load operation.
9950     loadRecords : function(o, options, success){
9951         if(!o || success === false){
9952             if(success !== false){
9953                 this.fireEvent("load", this, [], options, o);
9954             }
9955             if(options.callback){
9956                 options.callback.call(options.scope || this, [], options, false);
9957             }
9958             return;
9959         }
9960         // if data returned failure - throw an exception.
9961         if (o.success === false) {
9962             // show a message if no listener is registered.
9963             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9964                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9965             }
9966             // loadmask wil be hooked into this..
9967             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9968             return;
9969         }
9970         var r = o.records, t = o.totalRecords || r.length;
9971         
9972         this.fireEvent("beforeloadadd", this, r, options, o);
9973         
9974         if(!options || options.add !== true){
9975             if(this.pruneModifiedRecords){
9976                 this.modified = [];
9977             }
9978             for(var i = 0, len = r.length; i < len; i++){
9979                 r[i].join(this);
9980             }
9981             if(this.snapshot){
9982                 this.data = this.snapshot;
9983                 delete this.snapshot;
9984             }
9985             this.data.clear();
9986             this.data.addAll(r);
9987             this.totalLength = t;
9988             this.applySort();
9989             this.fireEvent("datachanged", this);
9990         }else{
9991             this.totalLength = Math.max(t, this.data.length+r.length);
9992             this.add(r);
9993         }
9994         this.fireEvent("load", this, r, options, o);
9995         if(options.callback){
9996             options.callback.call(options.scope || this, r, options, true);
9997         }
9998     },
9999
10000
10001     /**
10002      * Loads data from a passed data block. A Reader which understands the format of the data
10003      * must have been configured in the constructor.
10004      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10005      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10006      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10007      */
10008     loadData : function(o, append){
10009         var r = this.reader.readRecords(o);
10010         this.loadRecords(r, {add: append}, true);
10011     },
10012
10013     /**
10014      * Gets the number of cached records.
10015      * <p>
10016      * <em>If using paging, this may not be the total size of the dataset. If the data object
10017      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10018      * the data set size</em>
10019      */
10020     getCount : function(){
10021         return this.data.length || 0;
10022     },
10023
10024     /**
10025      * Gets the total number of records in the dataset as returned by the server.
10026      * <p>
10027      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10028      * the dataset size</em>
10029      */
10030     getTotalCount : function(){
10031         return this.totalLength || 0;
10032     },
10033
10034     /**
10035      * Returns the sort state of the Store as an object with two properties:
10036      * <pre><code>
10037  field {String} The name of the field by which the Records are sorted
10038  direction {String} The sort order, "ASC" or "DESC"
10039      * </code></pre>
10040      */
10041     getSortState : function(){
10042         return this.sortInfo;
10043     },
10044
10045     // private
10046     applySort : function(){
10047         if(this.sortInfo && !this.remoteSort){
10048             var s = this.sortInfo, f = s.field;
10049             var st = this.fields.get(f).sortType;
10050             var fn = function(r1, r2){
10051                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10052                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10053             };
10054             this.data.sort(s.direction, fn);
10055             if(this.snapshot && this.snapshot != this.data){
10056                 this.snapshot.sort(s.direction, fn);
10057             }
10058         }
10059     },
10060
10061     /**
10062      * Sets the default sort column and order to be used by the next load operation.
10063      * @param {String} fieldName The name of the field to sort by.
10064      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10065      */
10066     setDefaultSort : function(field, dir){
10067         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10068     },
10069
10070     /**
10071      * Sort the Records.
10072      * If remote sorting is used, the sort is performed on the server, and the cache is
10073      * reloaded. If local sorting is used, the cache is sorted internally.
10074      * @param {String} fieldName The name of the field to sort by.
10075      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10076      */
10077     sort : function(fieldName, dir){
10078         var f = this.fields.get(fieldName);
10079         if(!dir){
10080             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10081             
10082             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10083                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10084             }else{
10085                 dir = f.sortDir;
10086             }
10087         }
10088         this.sortToggle[f.name] = dir;
10089         this.sortInfo = {field: f.name, direction: dir};
10090         if(!this.remoteSort){
10091             this.applySort();
10092             this.fireEvent("datachanged", this);
10093         }else{
10094             this.load(this.lastOptions);
10095         }
10096     },
10097
10098     /**
10099      * Calls the specified function for each of the Records in the cache.
10100      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10101      * Returning <em>false</em> aborts and exits the iteration.
10102      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10103      */
10104     each : function(fn, scope){
10105         this.data.each(fn, scope);
10106     },
10107
10108     /**
10109      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10110      * (e.g., during paging).
10111      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10112      */
10113     getModifiedRecords : function(){
10114         return this.modified;
10115     },
10116
10117     // private
10118     createFilterFn : function(property, value, anyMatch){
10119         if(!value.exec){ // not a regex
10120             value = String(value);
10121             if(value.length == 0){
10122                 return false;
10123             }
10124             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10125         }
10126         return function(r){
10127             return value.test(r.data[property]);
10128         };
10129     },
10130
10131     /**
10132      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10133      * @param {String} property A field on your records
10134      * @param {Number} start The record index to start at (defaults to 0)
10135      * @param {Number} end The last record index to include (defaults to length - 1)
10136      * @return {Number} The sum
10137      */
10138     sum : function(property, start, end){
10139         var rs = this.data.items, v = 0;
10140         start = start || 0;
10141         end = (end || end === 0) ? end : rs.length-1;
10142
10143         for(var i = start; i <= end; i++){
10144             v += (rs[i].data[property] || 0);
10145         }
10146         return v;
10147     },
10148
10149     /**
10150      * Filter the records by a specified property.
10151      * @param {String} field A field on your records
10152      * @param {String/RegExp} value Either a string that the field
10153      * should start with or a RegExp to test against the field
10154      * @param {Boolean} anyMatch True to match any part not just the beginning
10155      */
10156     filter : function(property, value, anyMatch){
10157         var fn = this.createFilterFn(property, value, anyMatch);
10158         return fn ? this.filterBy(fn) : this.clearFilter();
10159     },
10160
10161     /**
10162      * Filter by a function. The specified function will be called with each
10163      * record in this data source. If the function returns true the record is included,
10164      * otherwise it is filtered.
10165      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10166      * @param {Object} scope (optional) The scope of the function (defaults to this)
10167      */
10168     filterBy : function(fn, scope){
10169         this.snapshot = this.snapshot || this.data;
10170         this.data = this.queryBy(fn, scope||this);
10171         this.fireEvent("datachanged", this);
10172     },
10173
10174     /**
10175      * Query the records by a specified property.
10176      * @param {String} field A field on your records
10177      * @param {String/RegExp} value Either a string that the field
10178      * should start with or a RegExp to test against the field
10179      * @param {Boolean} anyMatch True to match any part not just the beginning
10180      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10181      */
10182     query : function(property, value, anyMatch){
10183         var fn = this.createFilterFn(property, value, anyMatch);
10184         return fn ? this.queryBy(fn) : this.data.clone();
10185     },
10186
10187     /**
10188      * Query by a function. The specified function will be called with each
10189      * record in this data source. If the function returns true the record is included
10190      * in the results.
10191      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10192      * @param {Object} scope (optional) The scope of the function (defaults to this)
10193       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10194      **/
10195     queryBy : function(fn, scope){
10196         var data = this.snapshot || this.data;
10197         return data.filterBy(fn, scope||this);
10198     },
10199
10200     /**
10201      * Collects unique values for a particular dataIndex from this store.
10202      * @param {String} dataIndex The property to collect
10203      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10204      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10205      * @return {Array} An array of the unique values
10206      **/
10207     collect : function(dataIndex, allowNull, bypassFilter){
10208         var d = (bypassFilter === true && this.snapshot) ?
10209                 this.snapshot.items : this.data.items;
10210         var v, sv, r = [], l = {};
10211         for(var i = 0, len = d.length; i < len; i++){
10212             v = d[i].data[dataIndex];
10213             sv = String(v);
10214             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10215                 l[sv] = true;
10216                 r[r.length] = v;
10217             }
10218         }
10219         return r;
10220     },
10221
10222     /**
10223      * Revert to a view of the Record cache with no filtering applied.
10224      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10225      */
10226     clearFilter : function(suppressEvent){
10227         if(this.snapshot && this.snapshot != this.data){
10228             this.data = this.snapshot;
10229             delete this.snapshot;
10230             if(suppressEvent !== true){
10231                 this.fireEvent("datachanged", this);
10232             }
10233         }
10234     },
10235
10236     // private
10237     afterEdit : function(record){
10238         if(this.modified.indexOf(record) == -1){
10239             this.modified.push(record);
10240         }
10241         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10242     },
10243     
10244     // private
10245     afterReject : function(record){
10246         this.modified.remove(record);
10247         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10248     },
10249
10250     // private
10251     afterCommit : function(record){
10252         this.modified.remove(record);
10253         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10254     },
10255
10256     /**
10257      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10258      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10259      */
10260     commitChanges : function(){
10261         var m = this.modified.slice(0);
10262         this.modified = [];
10263         for(var i = 0, len = m.length; i < len; i++){
10264             m[i].commit();
10265         }
10266     },
10267
10268     /**
10269      * Cancel outstanding changes on all changed records.
10270      */
10271     rejectChanges : function(){
10272         var m = this.modified.slice(0);
10273         this.modified = [];
10274         for(var i = 0, len = m.length; i < len; i++){
10275             m[i].reject();
10276         }
10277     },
10278
10279     onMetaChange : function(meta, rtype, o){
10280         this.recordType = rtype;
10281         this.fields = rtype.prototype.fields;
10282         delete this.snapshot;
10283         this.sortInfo = meta.sortInfo || this.sortInfo;
10284         this.modified = [];
10285         this.fireEvent('metachange', this, this.reader.meta);
10286     },
10287     
10288     moveIndex : function(data, type)
10289     {
10290         var index = this.indexOf(data);
10291         
10292         var newIndex = index + type;
10293         
10294         this.remove(data);
10295         
10296         this.insert(newIndex, data);
10297         
10298     }
10299 });/*
10300  * Based on:
10301  * Ext JS Library 1.1.1
10302  * Copyright(c) 2006-2007, Ext JS, LLC.
10303  *
10304  * Originally Released Under LGPL - original licence link has changed is not relivant.
10305  *
10306  * Fork - LGPL
10307  * <script type="text/javascript">
10308  */
10309
10310 /**
10311  * @class Roo.data.SimpleStore
10312  * @extends Roo.data.Store
10313  * Small helper class to make creating Stores from Array data easier.
10314  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10315  * @cfg {Array} fields An array of field definition objects, or field name strings.
10316  * @cfg {Array} data The multi-dimensional array of data
10317  * @constructor
10318  * @param {Object} config
10319  */
10320 Roo.data.SimpleStore = function(config){
10321     Roo.data.SimpleStore.superclass.constructor.call(this, {
10322         isLocal : true,
10323         reader: new Roo.data.ArrayReader({
10324                 id: config.id
10325             },
10326             Roo.data.Record.create(config.fields)
10327         ),
10328         proxy : new Roo.data.MemoryProxy(config.data)
10329     });
10330     this.load();
10331 };
10332 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10333  * Based on:
10334  * Ext JS Library 1.1.1
10335  * Copyright(c) 2006-2007, Ext JS, LLC.
10336  *
10337  * Originally Released Under LGPL - original licence link has changed is not relivant.
10338  *
10339  * Fork - LGPL
10340  * <script type="text/javascript">
10341  */
10342
10343 /**
10344 /**
10345  * @extends Roo.data.Store
10346  * @class Roo.data.JsonStore
10347  * Small helper class to make creating Stores for JSON data easier. <br/>
10348 <pre><code>
10349 var store = new Roo.data.JsonStore({
10350     url: 'get-images.php',
10351     root: 'images',
10352     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10353 });
10354 </code></pre>
10355  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10356  * JsonReader and HttpProxy (unless inline data is provided).</b>
10357  * @cfg {Array} fields An array of field definition objects, or field name strings.
10358  * @constructor
10359  * @param {Object} config
10360  */
10361 Roo.data.JsonStore = function(c){
10362     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10363         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10364         reader: new Roo.data.JsonReader(c, c.fields)
10365     }));
10366 };
10367 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10368  * Based on:
10369  * Ext JS Library 1.1.1
10370  * Copyright(c) 2006-2007, Ext JS, LLC.
10371  *
10372  * Originally Released Under LGPL - original licence link has changed is not relivant.
10373  *
10374  * Fork - LGPL
10375  * <script type="text/javascript">
10376  */
10377
10378  
10379 Roo.data.Field = function(config){
10380     if(typeof config == "string"){
10381         config = {name: config};
10382     }
10383     Roo.apply(this, config);
10384     
10385     if(!this.type){
10386         this.type = "auto";
10387     }
10388     
10389     var st = Roo.data.SortTypes;
10390     // named sortTypes are supported, here we look them up
10391     if(typeof this.sortType == "string"){
10392         this.sortType = st[this.sortType];
10393     }
10394     
10395     // set default sortType for strings and dates
10396     if(!this.sortType){
10397         switch(this.type){
10398             case "string":
10399                 this.sortType = st.asUCString;
10400                 break;
10401             case "date":
10402                 this.sortType = st.asDate;
10403                 break;
10404             default:
10405                 this.sortType = st.none;
10406         }
10407     }
10408
10409     // define once
10410     var stripRe = /[\$,%]/g;
10411
10412     // prebuilt conversion function for this field, instead of
10413     // switching every time we're reading a value
10414     if(!this.convert){
10415         var cv, dateFormat = this.dateFormat;
10416         switch(this.type){
10417             case "":
10418             case "auto":
10419             case undefined:
10420                 cv = function(v){ return v; };
10421                 break;
10422             case "string":
10423                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10424                 break;
10425             case "int":
10426                 cv = function(v){
10427                     return v !== undefined && v !== null && v !== '' ?
10428                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10429                     };
10430                 break;
10431             case "float":
10432                 cv = function(v){
10433                     return v !== undefined && v !== null && v !== '' ?
10434                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10435                     };
10436                 break;
10437             case "bool":
10438             case "boolean":
10439                 cv = function(v){ return v === true || v === "true" || v == 1; };
10440                 break;
10441             case "date":
10442                 cv = function(v){
10443                     if(!v){
10444                         return '';
10445                     }
10446                     if(v instanceof Date){
10447                         return v;
10448                     }
10449                     if(dateFormat){
10450                         if(dateFormat == "timestamp"){
10451                             return new Date(v*1000);
10452                         }
10453                         return Date.parseDate(v, dateFormat);
10454                     }
10455                     var parsed = Date.parse(v);
10456                     return parsed ? new Date(parsed) : null;
10457                 };
10458              break;
10459             
10460         }
10461         this.convert = cv;
10462     }
10463 };
10464
10465 Roo.data.Field.prototype = {
10466     dateFormat: null,
10467     defaultValue: "",
10468     mapping: null,
10469     sortType : null,
10470     sortDir : "ASC"
10471 };/*
10472  * Based on:
10473  * Ext JS Library 1.1.1
10474  * Copyright(c) 2006-2007, Ext JS, LLC.
10475  *
10476  * Originally Released Under LGPL - original licence link has changed is not relivant.
10477  *
10478  * Fork - LGPL
10479  * <script type="text/javascript">
10480  */
10481  
10482 // Base class for reading structured data from a data source.  This class is intended to be
10483 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10484
10485 /**
10486  * @class Roo.data.DataReader
10487  * Base class for reading structured data from a data source.  This class is intended to be
10488  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10489  */
10490
10491 Roo.data.DataReader = function(meta, recordType){
10492     
10493     this.meta = meta;
10494     
10495     this.recordType = recordType instanceof Array ? 
10496         Roo.data.Record.create(recordType) : recordType;
10497 };
10498
10499 Roo.data.DataReader.prototype = {
10500      /**
10501      * Create an empty record
10502      * @param {Object} data (optional) - overlay some values
10503      * @return {Roo.data.Record} record created.
10504      */
10505     newRow :  function(d) {
10506         var da =  {};
10507         this.recordType.prototype.fields.each(function(c) {
10508             switch( c.type) {
10509                 case 'int' : da[c.name] = 0; break;
10510                 case 'date' : da[c.name] = new Date(); break;
10511                 case 'float' : da[c.name] = 0.0; break;
10512                 case 'boolean' : da[c.name] = false; break;
10513                 default : da[c.name] = ""; break;
10514             }
10515             
10516         });
10517         return new this.recordType(Roo.apply(da, d));
10518     }
10519     
10520 };/*
10521  * Based on:
10522  * Ext JS Library 1.1.1
10523  * Copyright(c) 2006-2007, Ext JS, LLC.
10524  *
10525  * Originally Released Under LGPL - original licence link has changed is not relivant.
10526  *
10527  * Fork - LGPL
10528  * <script type="text/javascript">
10529  */
10530
10531 /**
10532  * @class Roo.data.DataProxy
10533  * @extends Roo.data.Observable
10534  * This class is an abstract base class for implementations which provide retrieval of
10535  * unformatted data objects.<br>
10536  * <p>
10537  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10538  * (of the appropriate type which knows how to parse the data object) to provide a block of
10539  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10540  * <p>
10541  * Custom implementations must implement the load method as described in
10542  * {@link Roo.data.HttpProxy#load}.
10543  */
10544 Roo.data.DataProxy = function(){
10545     this.addEvents({
10546         /**
10547          * @event beforeload
10548          * Fires before a network request is made to retrieve a data object.
10549          * @param {Object} This DataProxy object.
10550          * @param {Object} params The params parameter to the load function.
10551          */
10552         beforeload : true,
10553         /**
10554          * @event load
10555          * Fires before the load method's callback is called.
10556          * @param {Object} This DataProxy object.
10557          * @param {Object} o The data object.
10558          * @param {Object} arg The callback argument object passed to the load function.
10559          */
10560         load : true,
10561         /**
10562          * @event loadexception
10563          * Fires if an Exception occurs during data retrieval.
10564          * @param {Object} This DataProxy object.
10565          * @param {Object} o The data object.
10566          * @param {Object} arg The callback argument object passed to the load function.
10567          * @param {Object} e The Exception.
10568          */
10569         loadexception : true
10570     });
10571     Roo.data.DataProxy.superclass.constructor.call(this);
10572 };
10573
10574 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10575
10576     /**
10577      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10578      */
10579 /*
10580  * Based on:
10581  * Ext JS Library 1.1.1
10582  * Copyright(c) 2006-2007, Ext JS, LLC.
10583  *
10584  * Originally Released Under LGPL - original licence link has changed is not relivant.
10585  *
10586  * Fork - LGPL
10587  * <script type="text/javascript">
10588  */
10589 /**
10590  * @class Roo.data.MemoryProxy
10591  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10592  * to the Reader when its load method is called.
10593  * @constructor
10594  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10595  */
10596 Roo.data.MemoryProxy = function(data){
10597     if (data.data) {
10598         data = data.data;
10599     }
10600     Roo.data.MemoryProxy.superclass.constructor.call(this);
10601     this.data = data;
10602 };
10603
10604 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10605     /**
10606      * Load data from the requested source (in this case an in-memory
10607      * data object passed to the constructor), read the data object into
10608      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10609      * process that block using the passed callback.
10610      * @param {Object} params This parameter is not used by the MemoryProxy class.
10611      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10612      * object into a block of Roo.data.Records.
10613      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10614      * The function must be passed <ul>
10615      * <li>The Record block object</li>
10616      * <li>The "arg" argument from the load function</li>
10617      * <li>A boolean success indicator</li>
10618      * </ul>
10619      * @param {Object} scope The scope in which to call the callback
10620      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10621      */
10622     load : function(params, reader, callback, scope, arg){
10623         params = params || {};
10624         var result;
10625         try {
10626             result = reader.readRecords(this.data);
10627         }catch(e){
10628             this.fireEvent("loadexception", this, arg, null, e);
10629             callback.call(scope, null, arg, false);
10630             return;
10631         }
10632         callback.call(scope, result, arg, true);
10633     },
10634     
10635     // private
10636     update : function(params, records){
10637         
10638     }
10639 });/*
10640  * Based on:
10641  * Ext JS Library 1.1.1
10642  * Copyright(c) 2006-2007, Ext JS, LLC.
10643  *
10644  * Originally Released Under LGPL - original licence link has changed is not relivant.
10645  *
10646  * Fork - LGPL
10647  * <script type="text/javascript">
10648  */
10649 /**
10650  * @class Roo.data.HttpProxy
10651  * @extends Roo.data.DataProxy
10652  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10653  * configured to reference a certain URL.<br><br>
10654  * <p>
10655  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10656  * from which the running page was served.<br><br>
10657  * <p>
10658  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10659  * <p>
10660  * Be aware that to enable the browser to parse an XML document, the server must set
10661  * the Content-Type header in the HTTP response to "text/xml".
10662  * @constructor
10663  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10664  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10665  * will be used to make the request.
10666  */
10667 Roo.data.HttpProxy = function(conn){
10668     Roo.data.HttpProxy.superclass.constructor.call(this);
10669     // is conn a conn config or a real conn?
10670     this.conn = conn;
10671     this.useAjax = !conn || !conn.events;
10672   
10673 };
10674
10675 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10676     // thse are take from connection...
10677     
10678     /**
10679      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10680      */
10681     /**
10682      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10683      * extra parameters to each request made by this object. (defaults to undefined)
10684      */
10685     /**
10686      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10687      *  to each request made by this object. (defaults to undefined)
10688      */
10689     /**
10690      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
10691      */
10692     /**
10693      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10694      */
10695      /**
10696      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10697      * @type Boolean
10698      */
10699   
10700
10701     /**
10702      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10703      * @type Boolean
10704      */
10705     /**
10706      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10707      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10708      * a finer-grained basis than the DataProxy events.
10709      */
10710     getConnection : function(){
10711         return this.useAjax ? Roo.Ajax : this.conn;
10712     },
10713
10714     /**
10715      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10716      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10717      * process that block using the passed callback.
10718      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10719      * for the request to the remote server.
10720      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10721      * object into a block of Roo.data.Records.
10722      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10723      * The function must be passed <ul>
10724      * <li>The Record block object</li>
10725      * <li>The "arg" argument from the load function</li>
10726      * <li>A boolean success indicator</li>
10727      * </ul>
10728      * @param {Object} scope The scope in which to call the callback
10729      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10730      */
10731     load : function(params, reader, callback, scope, arg){
10732         if(this.fireEvent("beforeload", this, params) !== false){
10733             var  o = {
10734                 params : params || {},
10735                 request: {
10736                     callback : callback,
10737                     scope : scope,
10738                     arg : arg
10739                 },
10740                 reader: reader,
10741                 callback : this.loadResponse,
10742                 scope: this
10743             };
10744             if(this.useAjax){
10745                 Roo.applyIf(o, this.conn);
10746                 if(this.activeRequest){
10747                     Roo.Ajax.abort(this.activeRequest);
10748                 }
10749                 this.activeRequest = Roo.Ajax.request(o);
10750             }else{
10751                 this.conn.request(o);
10752             }
10753         }else{
10754             callback.call(scope||this, null, arg, false);
10755         }
10756     },
10757
10758     // private
10759     loadResponse : function(o, success, response){
10760         delete this.activeRequest;
10761         if(!success){
10762             this.fireEvent("loadexception", this, o, response);
10763             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10764             return;
10765         }
10766         var result;
10767         try {
10768             result = o.reader.read(response);
10769         }catch(e){
10770             this.fireEvent("loadexception", this, o, response, e);
10771             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10772             return;
10773         }
10774         
10775         this.fireEvent("load", this, o, o.request.arg);
10776         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10777     },
10778
10779     // private
10780     update : function(dataSet){
10781
10782     },
10783
10784     // private
10785     updateResponse : function(dataSet){
10786
10787     }
10788 });/*
10789  * Based on:
10790  * Ext JS Library 1.1.1
10791  * Copyright(c) 2006-2007, Ext JS, LLC.
10792  *
10793  * Originally Released Under LGPL - original licence link has changed is not relivant.
10794  *
10795  * Fork - LGPL
10796  * <script type="text/javascript">
10797  */
10798
10799 /**
10800  * @class Roo.data.ScriptTagProxy
10801  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10802  * other than the originating domain of the running page.<br><br>
10803  * <p>
10804  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
10805  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10806  * <p>
10807  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10808  * source code that is used as the source inside a &lt;script> tag.<br><br>
10809  * <p>
10810  * In order for the browser to process the returned data, the server must wrap the data object
10811  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10812  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10813  * depending on whether the callback name was passed:
10814  * <p>
10815  * <pre><code>
10816 boolean scriptTag = false;
10817 String cb = request.getParameter("callback");
10818 if (cb != null) {
10819     scriptTag = true;
10820     response.setContentType("text/javascript");
10821 } else {
10822     response.setContentType("application/x-json");
10823 }
10824 Writer out = response.getWriter();
10825 if (scriptTag) {
10826     out.write(cb + "(");
10827 }
10828 out.print(dataBlock.toJsonString());
10829 if (scriptTag) {
10830     out.write(");");
10831 }
10832 </pre></code>
10833  *
10834  * @constructor
10835  * @param {Object} config A configuration object.
10836  */
10837 Roo.data.ScriptTagProxy = function(config){
10838     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10839     Roo.apply(this, config);
10840     this.head = document.getElementsByTagName("head")[0];
10841 };
10842
10843 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10844
10845 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10846     /**
10847      * @cfg {String} url The URL from which to request the data object.
10848      */
10849     /**
10850      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10851      */
10852     timeout : 30000,
10853     /**
10854      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10855      * the server the name of the callback function set up by the load call to process the returned data object.
10856      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10857      * javascript output which calls this named function passing the data object as its only parameter.
10858      */
10859     callbackParam : "callback",
10860     /**
10861      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10862      * name to the request.
10863      */
10864     nocache : true,
10865
10866     /**
10867      * Load data from the configured URL, read the data object into
10868      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10869      * process that block using the passed callback.
10870      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10871      * for the request to the remote server.
10872      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10873      * object into a block of Roo.data.Records.
10874      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10875      * The function must be passed <ul>
10876      * <li>The Record block object</li>
10877      * <li>The "arg" argument from the load function</li>
10878      * <li>A boolean success indicator</li>
10879      * </ul>
10880      * @param {Object} scope The scope in which to call the callback
10881      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10882      */
10883     load : function(params, reader, callback, scope, arg){
10884         if(this.fireEvent("beforeload", this, params) !== false){
10885
10886             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10887
10888             var url = this.url;
10889             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10890             if(this.nocache){
10891                 url += "&_dc=" + (new Date().getTime());
10892             }
10893             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10894             var trans = {
10895                 id : transId,
10896                 cb : "stcCallback"+transId,
10897                 scriptId : "stcScript"+transId,
10898                 params : params,
10899                 arg : arg,
10900                 url : url,
10901                 callback : callback,
10902                 scope : scope,
10903                 reader : reader
10904             };
10905             var conn = this;
10906
10907             window[trans.cb] = function(o){
10908                 conn.handleResponse(o, trans);
10909             };
10910
10911             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10912
10913             if(this.autoAbort !== false){
10914                 this.abort();
10915             }
10916
10917             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10918
10919             var script = document.createElement("script");
10920             script.setAttribute("src", url);
10921             script.setAttribute("type", "text/javascript");
10922             script.setAttribute("id", trans.scriptId);
10923             this.head.appendChild(script);
10924
10925             this.trans = trans;
10926         }else{
10927             callback.call(scope||this, null, arg, false);
10928         }
10929     },
10930
10931     // private
10932     isLoading : function(){
10933         return this.trans ? true : false;
10934     },
10935
10936     /**
10937      * Abort the current server request.
10938      */
10939     abort : function(){
10940         if(this.isLoading()){
10941             this.destroyTrans(this.trans);
10942         }
10943     },
10944
10945     // private
10946     destroyTrans : function(trans, isLoaded){
10947         this.head.removeChild(document.getElementById(trans.scriptId));
10948         clearTimeout(trans.timeoutId);
10949         if(isLoaded){
10950             window[trans.cb] = undefined;
10951             try{
10952                 delete window[trans.cb];
10953             }catch(e){}
10954         }else{
10955             // if hasn't been loaded, wait for load to remove it to prevent script error
10956             window[trans.cb] = function(){
10957                 window[trans.cb] = undefined;
10958                 try{
10959                     delete window[trans.cb];
10960                 }catch(e){}
10961             };
10962         }
10963     },
10964
10965     // private
10966     handleResponse : function(o, trans){
10967         this.trans = false;
10968         this.destroyTrans(trans, true);
10969         var result;
10970         try {
10971             result = trans.reader.readRecords(o);
10972         }catch(e){
10973             this.fireEvent("loadexception", this, o, trans.arg, e);
10974             trans.callback.call(trans.scope||window, null, trans.arg, false);
10975             return;
10976         }
10977         this.fireEvent("load", this, o, trans.arg);
10978         trans.callback.call(trans.scope||window, result, trans.arg, true);
10979     },
10980
10981     // private
10982     handleFailure : function(trans){
10983         this.trans = false;
10984         this.destroyTrans(trans, false);
10985         this.fireEvent("loadexception", this, null, trans.arg);
10986         trans.callback.call(trans.scope||window, null, trans.arg, false);
10987     }
10988 });/*
10989  * Based on:
10990  * Ext JS Library 1.1.1
10991  * Copyright(c) 2006-2007, Ext JS, LLC.
10992  *
10993  * Originally Released Under LGPL - original licence link has changed is not relivant.
10994  *
10995  * Fork - LGPL
10996  * <script type="text/javascript">
10997  */
10998
10999 /**
11000  * @class Roo.data.JsonReader
11001  * @extends Roo.data.DataReader
11002  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11003  * based on mappings in a provided Roo.data.Record constructor.
11004  * 
11005  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11006  * in the reply previously. 
11007  * 
11008  * <p>
11009  * Example code:
11010  * <pre><code>
11011 var RecordDef = Roo.data.Record.create([
11012     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11013     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11014 ]);
11015 var myReader = new Roo.data.JsonReader({
11016     totalProperty: "results",    // The property which contains the total dataset size (optional)
11017     root: "rows",                // The property which contains an Array of row objects
11018     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11019 }, RecordDef);
11020 </code></pre>
11021  * <p>
11022  * This would consume a JSON file like this:
11023  * <pre><code>
11024 { 'results': 2, 'rows': [
11025     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11026     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11027 }
11028 </code></pre>
11029  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11030  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11031  * paged from the remote server.
11032  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11033  * @cfg {String} root name of the property which contains the Array of row objects.
11034  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11035  * @cfg {Array} fields Array of field definition objects
11036  * @constructor
11037  * Create a new JsonReader
11038  * @param {Object} meta Metadata configuration options
11039  * @param {Object} recordType Either an Array of field definition objects,
11040  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11041  */
11042 Roo.data.JsonReader = function(meta, recordType){
11043     
11044     meta = meta || {};
11045     // set some defaults:
11046     Roo.applyIf(meta, {
11047         totalProperty: 'total',
11048         successProperty : 'success',
11049         root : 'data',
11050         id : 'id'
11051     });
11052     
11053     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11054 };
11055 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11056     
11057     /**
11058      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11059      * Used by Store query builder to append _requestMeta to params.
11060      * 
11061      */
11062     metaFromRemote : false,
11063     /**
11064      * This method is only used by a DataProxy which has retrieved data from a remote server.
11065      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11066      * @return {Object} data A data block which is used by an Roo.data.Store object as
11067      * a cache of Roo.data.Records.
11068      */
11069     read : function(response){
11070         var json = response.responseText;
11071        
11072         var o = /* eval:var:o */ eval("("+json+")");
11073         if(!o) {
11074             throw {message: "JsonReader.read: Json object not found"};
11075         }
11076         
11077         if(o.metaData){
11078             
11079             delete this.ef;
11080             this.metaFromRemote = true;
11081             this.meta = o.metaData;
11082             this.recordType = Roo.data.Record.create(o.metaData.fields);
11083             this.onMetaChange(this.meta, this.recordType, o);
11084         }
11085         return this.readRecords(o);
11086     },
11087
11088     // private function a store will implement
11089     onMetaChange : function(meta, recordType, o){
11090
11091     },
11092
11093     /**
11094          * @ignore
11095          */
11096     simpleAccess: function(obj, subsc) {
11097         return obj[subsc];
11098     },
11099
11100         /**
11101          * @ignore
11102          */
11103     getJsonAccessor: function(){
11104         var re = /[\[\.]/;
11105         return function(expr) {
11106             try {
11107                 return(re.test(expr))
11108                     ? new Function("obj", "return obj." + expr)
11109                     : function(obj){
11110                         return obj[expr];
11111                     };
11112             } catch(e){}
11113             return Roo.emptyFn;
11114         };
11115     }(),
11116
11117     /**
11118      * Create a data block containing Roo.data.Records from an XML document.
11119      * @param {Object} o An object which contains an Array of row objects in the property specified
11120      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11121      * which contains the total size of the dataset.
11122      * @return {Object} data A data block which is used by an Roo.data.Store object as
11123      * a cache of Roo.data.Records.
11124      */
11125     readRecords : function(o){
11126         /**
11127          * After any data loads, the raw JSON data is available for further custom processing.
11128          * @type Object
11129          */
11130         this.o = o;
11131         var s = this.meta, Record = this.recordType,
11132             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11133
11134 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11135         if (!this.ef) {
11136             if(s.totalProperty) {
11137                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11138                 }
11139                 if(s.successProperty) {
11140                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11141                 }
11142                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11143                 if (s.id) {
11144                         var g = this.getJsonAccessor(s.id);
11145                         this.getId = function(rec) {
11146                                 var r = g(rec);  
11147                                 return (r === undefined || r === "") ? null : r;
11148                         };
11149                 } else {
11150                         this.getId = function(){return null;};
11151                 }
11152             this.ef = [];
11153             for(var jj = 0; jj < fl; jj++){
11154                 f = fi[jj];
11155                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11156                 this.ef[jj] = this.getJsonAccessor(map);
11157             }
11158         }
11159
11160         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11161         if(s.totalProperty){
11162             var vt = parseInt(this.getTotal(o), 10);
11163             if(!isNaN(vt)){
11164                 totalRecords = vt;
11165             }
11166         }
11167         if(s.successProperty){
11168             var vs = this.getSuccess(o);
11169             if(vs === false || vs === 'false'){
11170                 success = false;
11171             }
11172         }
11173         var records = [];
11174         for(var i = 0; i < c; i++){
11175                 var n = root[i];
11176             var values = {};
11177             var id = this.getId(n);
11178             for(var j = 0; j < fl; j++){
11179                 f = fi[j];
11180             var v = this.ef[j](n);
11181             if (!f.convert) {
11182                 Roo.log('missing convert for ' + f.name);
11183                 Roo.log(f);
11184                 continue;
11185             }
11186             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11187             }
11188             var record = new Record(values, id);
11189             record.json = n;
11190             records[i] = record;
11191         }
11192         return {
11193             raw : o,
11194             success : success,
11195             records : records,
11196             totalRecords : totalRecords
11197         };
11198     }
11199 });/*
11200  * Based on:
11201  * Ext JS Library 1.1.1
11202  * Copyright(c) 2006-2007, Ext JS, LLC.
11203  *
11204  * Originally Released Under LGPL - original licence link has changed is not relivant.
11205  *
11206  * Fork - LGPL
11207  * <script type="text/javascript">
11208  */
11209
11210 /**
11211  * @class Roo.data.ArrayReader
11212  * @extends Roo.data.DataReader
11213  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11214  * Each element of that Array represents a row of data fields. The
11215  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11216  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11217  * <p>
11218  * Example code:.
11219  * <pre><code>
11220 var RecordDef = Roo.data.Record.create([
11221     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11222     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11223 ]);
11224 var myReader = new Roo.data.ArrayReader({
11225     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11226 }, RecordDef);
11227 </code></pre>
11228  * <p>
11229  * This would consume an Array like this:
11230  * <pre><code>
11231 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11232   </code></pre>
11233  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11234  * @constructor
11235  * Create a new JsonReader
11236  * @param {Object} meta Metadata configuration options.
11237  * @param {Object} recordType Either an Array of field definition objects
11238  * as specified to {@link Roo.data.Record#create},
11239  * or an {@link Roo.data.Record} object
11240  * created using {@link Roo.data.Record#create}.
11241  */
11242 Roo.data.ArrayReader = function(meta, recordType){
11243     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11244 };
11245
11246 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11247     /**
11248      * Create a data block containing Roo.data.Records from an XML document.
11249      * @param {Object} o An Array of row objects which represents the dataset.
11250      * @return {Object} data A data block which is used by an Roo.data.Store object as
11251      * a cache of Roo.data.Records.
11252      */
11253     readRecords : function(o){
11254         var sid = this.meta ? this.meta.id : null;
11255         var recordType = this.recordType, fields = recordType.prototype.fields;
11256         var records = [];
11257         var root = o;
11258             for(var i = 0; i < root.length; i++){
11259                     var n = root[i];
11260                 var values = {};
11261                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11262                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11263                 var f = fields.items[j];
11264                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11265                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11266                 v = f.convert(v);
11267                 values[f.name] = v;
11268             }
11269                 var record = new recordType(values, id);
11270                 record.json = n;
11271                 records[records.length] = record;
11272             }
11273             return {
11274                 records : records,
11275                 totalRecords : records.length
11276             };
11277     }
11278 });/*
11279  * - LGPL
11280  * * 
11281  */
11282
11283 /**
11284  * @class Roo.bootstrap.ComboBox
11285  * @extends Roo.bootstrap.TriggerField
11286  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11287  * @cfg {Boolean} append (true|false) default false
11288  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11289  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11290  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11291  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11292  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11293  * @cfg {Boolean} animate default true
11294  * @cfg {Boolean} emptyResultText only for touch device
11295  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11296  * @constructor
11297  * Create a new ComboBox.
11298  * @param {Object} config Configuration options
11299  */
11300 Roo.bootstrap.ComboBox = function(config){
11301     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11302     this.addEvents({
11303         /**
11304          * @event expand
11305          * Fires when the dropdown list is expanded
11306              * @param {Roo.bootstrap.ComboBox} combo This combo box
11307              */
11308         'expand' : true,
11309         /**
11310          * @event collapse
11311          * Fires when the dropdown list is collapsed
11312              * @param {Roo.bootstrap.ComboBox} combo This combo box
11313              */
11314         'collapse' : true,
11315         /**
11316          * @event beforeselect
11317          * Fires before a list item is selected. Return false to cancel the selection.
11318              * @param {Roo.bootstrap.ComboBox} combo This combo box
11319              * @param {Roo.data.Record} record The data record returned from the underlying store
11320              * @param {Number} index The index of the selected item in the dropdown list
11321              */
11322         'beforeselect' : true,
11323         /**
11324          * @event select
11325          * Fires when a list item is selected
11326              * @param {Roo.bootstrap.ComboBox} combo This combo box
11327              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11328              * @param {Number} index The index of the selected item in the dropdown list
11329              */
11330         'select' : true,
11331         /**
11332          * @event beforequery
11333          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11334          * The event object passed has these properties:
11335              * @param {Roo.bootstrap.ComboBox} combo This combo box
11336              * @param {String} query The query
11337              * @param {Boolean} forceAll true to force "all" query
11338              * @param {Boolean} cancel true to cancel the query
11339              * @param {Object} e The query event object
11340              */
11341         'beforequery': true,
11342          /**
11343          * @event add
11344          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11345              * @param {Roo.bootstrap.ComboBox} combo This combo box
11346              */
11347         'add' : true,
11348         /**
11349          * @event edit
11350          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11351              * @param {Roo.bootstrap.ComboBox} combo This combo box
11352              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11353              */
11354         'edit' : true,
11355         /**
11356          * @event remove
11357          * Fires when the remove value from the combobox array
11358              * @param {Roo.bootstrap.ComboBox} combo This combo box
11359              */
11360         'remove' : true,
11361         /**
11362          * @event specialfilter
11363          * Fires when specialfilter
11364             * @param {Roo.bootstrap.ComboBox} combo This combo box
11365             */
11366         'specialfilter' : true,
11367         /**
11368          * @event tick
11369          * Fires when tick the element
11370             * @param {Roo.bootstrap.ComboBox} combo This combo box
11371             */
11372         'tick' : true
11373         
11374     });
11375     
11376     this.item = [];
11377     this.tickItems = [];
11378     
11379     this.selectedIndex = -1;
11380     if(this.mode == 'local'){
11381         if(config.queryDelay === undefined){
11382             this.queryDelay = 10;
11383         }
11384         if(config.minChars === undefined){
11385             this.minChars = 0;
11386         }
11387     }
11388 };
11389
11390 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11391      
11392     /**
11393      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11394      * rendering into an Roo.Editor, defaults to false)
11395      */
11396     /**
11397      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11398      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11399      */
11400     /**
11401      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11402      */
11403     /**
11404      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11405      * the dropdown list (defaults to undefined, with no header element)
11406      */
11407
11408      /**
11409      * @cfg {String/Roo.Template} tpl The template to use to render the output
11410      */
11411      
11412      /**
11413      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11414      */
11415     listWidth: undefined,
11416     /**
11417      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11418      * mode = 'remote' or 'text' if mode = 'local')
11419      */
11420     displayField: undefined,
11421     
11422     /**
11423      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11424      * mode = 'remote' or 'value' if mode = 'local'). 
11425      * Note: use of a valueField requires the user make a selection
11426      * in order for a value to be mapped.
11427      */
11428     valueField: undefined,
11429     
11430     
11431     /**
11432      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11433      * field's data value (defaults to the underlying DOM element's name)
11434      */
11435     hiddenName: undefined,
11436     /**
11437      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11438      */
11439     listClass: '',
11440     /**
11441      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11442      */
11443     selectedClass: 'active',
11444     
11445     /**
11446      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11447      */
11448     shadow:'sides',
11449     /**
11450      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11451      * anchor positions (defaults to 'tl-bl')
11452      */
11453     listAlign: 'tl-bl?',
11454     /**
11455      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11456      */
11457     maxHeight: 300,
11458     /**
11459      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11460      * query specified by the allQuery config option (defaults to 'query')
11461      */
11462     triggerAction: 'query',
11463     /**
11464      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11465      * (defaults to 4, does not apply if editable = false)
11466      */
11467     minChars : 4,
11468     /**
11469      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11470      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11471      */
11472     typeAhead: false,
11473     /**
11474      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11475      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11476      */
11477     queryDelay: 500,
11478     /**
11479      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11480      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11481      */
11482     pageSize: 0,
11483     /**
11484      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11485      * when editable = true (defaults to false)
11486      */
11487     selectOnFocus:false,
11488     /**
11489      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11490      */
11491     queryParam: 'query',
11492     /**
11493      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11494      * when mode = 'remote' (defaults to 'Loading...')
11495      */
11496     loadingText: 'Loading...',
11497     /**
11498      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11499      */
11500     resizable: false,
11501     /**
11502      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11503      */
11504     handleHeight : 8,
11505     /**
11506      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11507      * traditional select (defaults to true)
11508      */
11509     editable: true,
11510     /**
11511      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11512      */
11513     allQuery: '',
11514     /**
11515      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11516      */
11517     mode: 'remote',
11518     /**
11519      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11520      * listWidth has a higher value)
11521      */
11522     minListWidth : 70,
11523     /**
11524      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11525      * allow the user to set arbitrary text into the field (defaults to false)
11526      */
11527     forceSelection:false,
11528     /**
11529      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11530      * if typeAhead = true (defaults to 250)
11531      */
11532     typeAheadDelay : 250,
11533     /**
11534      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11535      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11536      */
11537     valueNotFoundText : undefined,
11538     /**
11539      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11540      */
11541     blockFocus : false,
11542     
11543     /**
11544      * @cfg {Boolean} disableClear Disable showing of clear button.
11545      */
11546     disableClear : false,
11547     /**
11548      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11549      */
11550     alwaysQuery : false,
11551     
11552     /**
11553      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11554      */
11555     multiple : false,
11556     
11557     /**
11558      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11559      */
11560     invalidClass : "has-warning",
11561     
11562     /**
11563      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11564      */
11565     validClass : "has-success",
11566     
11567     /**
11568      * @cfg {Boolean} specialFilter (true|false) special filter default false
11569      */
11570     specialFilter : false,
11571     
11572     /**
11573      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11574      */
11575     mobileTouchView : true,
11576     
11577     //private
11578     addicon : false,
11579     editicon: false,
11580     
11581     page: 0,
11582     hasQuery: false,
11583     append: false,
11584     loadNext: false,
11585     autoFocus : true,
11586     tickable : false,
11587     btnPosition : 'right',
11588     triggerList : true,
11589     showToggleBtn : true,
11590     animate : true,
11591     emptyResultText: 'Empty',
11592     triggerText : 'Select',
11593     
11594     // element that contains real text value.. (when hidden is used..)
11595     
11596     getAutoCreate : function()
11597     {
11598         var cfg = false;
11599         
11600         /*
11601          * Touch Devices
11602          */
11603         
11604         if(Roo.isTouch && this.mobileTouchView){
11605             cfg = this.getAutoCreateTouchView();
11606             return cfg;;
11607         }
11608         
11609         /*
11610          *  Normal ComboBox
11611          */
11612         if(!this.tickable){
11613             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11614             return cfg;
11615         }
11616         
11617         /*
11618          *  ComboBox with tickable selections
11619          */
11620              
11621         var align = this.labelAlign || this.parentLabelAlign();
11622         
11623         cfg = {
11624             cls : 'form-group roo-combobox-tickable' //input-group
11625         };
11626         
11627         var buttons = {
11628             tag : 'div',
11629             cls : 'tickable-buttons',
11630             cn : [
11631                 {
11632                     tag : 'button',
11633                     type : 'button',
11634                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11635                     html : this.triggerText
11636                 },
11637                 {
11638                     tag : 'button',
11639                     type : 'button',
11640                     name : 'ok',
11641                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11642                     html : 'Done'
11643                 },
11644                 {
11645                     tag : 'button',
11646                     type : 'button',
11647                     name : 'cancel',
11648                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11649                     html : 'Cancel'
11650                 }
11651             ]
11652         };
11653         
11654         if(this.editable){
11655             buttons.cn.unshift({
11656                 tag: 'input',
11657                 cls: 'select2-search-field-input'
11658             });
11659         }
11660         
11661         var _this = this;
11662         
11663         Roo.each(buttons.cn, function(c){
11664             if (_this.size) {
11665                 c.cls += ' btn-' + _this.size;
11666             }
11667
11668             if (_this.disabled) {
11669                 c.disabled = true;
11670             }
11671         });
11672         
11673         var box = {
11674             tag: 'div',
11675             cn: [
11676                 {
11677                     tag: 'input',
11678                     type : 'hidden',
11679                     cls: 'form-hidden-field'
11680                 },
11681                 {
11682                     tag: 'ul',
11683                     cls: 'select2-choices',
11684                     cn:[
11685                         {
11686                             tag: 'li',
11687                             cls: 'select2-search-field',
11688                             cn: [
11689
11690                                 buttons
11691                             ]
11692                         }
11693                     ]
11694                 }
11695             ]
11696         };
11697         
11698         var combobox = {
11699             cls: 'select2-container input-group select2-container-multi',
11700             cn: [
11701                 box
11702 //                {
11703 //                    tag: 'ul',
11704 //                    cls: 'typeahead typeahead-long dropdown-menu',
11705 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11706 //                }
11707             ]
11708         };
11709         
11710         if(this.hasFeedback && !this.allowBlank){
11711             
11712             var feedback = {
11713                 tag: 'span',
11714                 cls: 'glyphicon form-control-feedback'
11715             };
11716
11717             combobox.cn.push(feedback);
11718         }
11719         
11720         if (align ==='left' && this.fieldLabel.length) {
11721             
11722                 Roo.log("left and has label");
11723                 cfg.cn = [
11724                     
11725                     {
11726                         tag: 'label',
11727                         'for' :  id,
11728                         cls : 'control-label col-sm-' + this.labelWidth,
11729                         html : this.fieldLabel
11730                         
11731                     },
11732                     {
11733                         cls : "col-sm-" + (12 - this.labelWidth), 
11734                         cn: [
11735                             combobox
11736                         ]
11737                     }
11738                     
11739                 ];
11740         } else if ( this.fieldLabel.length) {
11741                 Roo.log(" label");
11742                  cfg.cn = [
11743                    
11744                     {
11745                         tag: 'label',
11746                         //cls : 'input-group-addon',
11747                         html : this.fieldLabel
11748                         
11749                     },
11750                     
11751                     combobox
11752                     
11753                 ];
11754
11755         } else {
11756             
11757                 Roo.log(" no label && no align");
11758                 cfg = combobox
11759                      
11760                 
11761         }
11762          
11763         var settings=this;
11764         ['xs','sm','md','lg'].map(function(size){
11765             if (settings[size]) {
11766                 cfg.cls += ' col-' + size + '-' + settings[size];
11767             }
11768         });
11769         
11770         return cfg;
11771         
11772     },
11773     
11774     _initEventsCalled : false,
11775     
11776     // private
11777     initEvents: function()
11778     {
11779         
11780         if (this._initEventsCalled) { // as we call render... prevent looping...
11781             return;
11782         }
11783         this._initEventsCalled = true;
11784         
11785         if (!this.store) {
11786             throw "can not find store for combo";
11787         }
11788         
11789         this.store = Roo.factory(this.store, Roo.data);
11790         
11791         // if we are building from html. then this element is so complex, that we can not really
11792         // use the rendered HTML.
11793         // so we have to trash and replace the previous code.
11794         if (Roo.XComponent.build_from_html) {
11795             
11796             // remove this element....
11797             var e = this.el.dom, k=0;
11798             while (e ) { e = e.previousSibling;  ++k;}
11799
11800             this.el.remove();
11801             
11802             this.el=false;
11803             this.rendered = false;
11804             
11805             this.render(this.parent().getChildContainer(true), k);
11806             
11807             
11808             
11809         }
11810         
11811         
11812         /*
11813          * Touch Devices
11814          */
11815         
11816         if(Roo.isTouch && this.mobileTouchView){
11817             this.initTouchView();
11818             return;
11819         }
11820         
11821         if(this.tickable){
11822             this.initTickableEvents();
11823             return;
11824         }
11825         
11826         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11827         
11828         if(this.hiddenName){
11829             
11830             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11831             
11832             this.hiddenField.dom.value =
11833                 this.hiddenValue !== undefined ? this.hiddenValue :
11834                 this.value !== undefined ? this.value : '';
11835
11836             // prevent input submission
11837             this.el.dom.removeAttribute('name');
11838             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11839              
11840              
11841         }
11842         //if(Roo.isGecko){
11843         //    this.el.dom.setAttribute('autocomplete', 'off');
11844         //}
11845         
11846         var cls = 'x-combo-list';
11847         
11848         //this.list = new Roo.Layer({
11849         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11850         //});
11851         
11852         var _this = this;
11853         
11854         (function(){
11855             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11856             _this.list.setWidth(lw);
11857         }).defer(100);
11858         
11859         this.list.on('mouseover', this.onViewOver, this);
11860         this.list.on('mousemove', this.onViewMove, this);
11861         
11862         this.list.on('scroll', this.onViewScroll, this);
11863         
11864         /*
11865         this.list.swallowEvent('mousewheel');
11866         this.assetHeight = 0;
11867
11868         if(this.title){
11869             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11870             this.assetHeight += this.header.getHeight();
11871         }
11872
11873         this.innerList = this.list.createChild({cls:cls+'-inner'});
11874         this.innerList.on('mouseover', this.onViewOver, this);
11875         this.innerList.on('mousemove', this.onViewMove, this);
11876         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11877         
11878         if(this.allowBlank && !this.pageSize && !this.disableClear){
11879             this.footer = this.list.createChild({cls:cls+'-ft'});
11880             this.pageTb = new Roo.Toolbar(this.footer);
11881            
11882         }
11883         if(this.pageSize){
11884             this.footer = this.list.createChild({cls:cls+'-ft'});
11885             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11886                     {pageSize: this.pageSize});
11887             
11888         }
11889         
11890         if (this.pageTb && this.allowBlank && !this.disableClear) {
11891             var _this = this;
11892             this.pageTb.add(new Roo.Toolbar.Fill(), {
11893                 cls: 'x-btn-icon x-btn-clear',
11894                 text: '&#160;',
11895                 handler: function()
11896                 {
11897                     _this.collapse();
11898                     _this.clearValue();
11899                     _this.onSelect(false, -1);
11900                 }
11901             });
11902         }
11903         if (this.footer) {
11904             this.assetHeight += this.footer.getHeight();
11905         }
11906         */
11907             
11908         if(!this.tpl){
11909             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11910         }
11911
11912         this.view = new Roo.View(this.list, this.tpl, {
11913             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11914         });
11915         //this.view.wrapEl.setDisplayed(false);
11916         this.view.on('click', this.onViewClick, this);
11917         
11918         
11919         
11920         this.store.on('beforeload', this.onBeforeLoad, this);
11921         this.store.on('load', this.onLoad, this);
11922         this.store.on('loadexception', this.onLoadException, this);
11923         /*
11924         if(this.resizable){
11925             this.resizer = new Roo.Resizable(this.list,  {
11926                pinned:true, handles:'se'
11927             });
11928             this.resizer.on('resize', function(r, w, h){
11929                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11930                 this.listWidth = w;
11931                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11932                 this.restrictHeight();
11933             }, this);
11934             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11935         }
11936         */
11937         if(!this.editable){
11938             this.editable = true;
11939             this.setEditable(false);
11940         }
11941         
11942         /*
11943         
11944         if (typeof(this.events.add.listeners) != 'undefined') {
11945             
11946             this.addicon = this.wrap.createChild(
11947                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11948        
11949             this.addicon.on('click', function(e) {
11950                 this.fireEvent('add', this);
11951             }, this);
11952         }
11953         if (typeof(this.events.edit.listeners) != 'undefined') {
11954             
11955             this.editicon = this.wrap.createChild(
11956                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11957             if (this.addicon) {
11958                 this.editicon.setStyle('margin-left', '40px');
11959             }
11960             this.editicon.on('click', function(e) {
11961                 
11962                 // we fire even  if inothing is selected..
11963                 this.fireEvent('edit', this, this.lastData );
11964                 
11965             }, this);
11966         }
11967         */
11968         
11969         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11970             "up" : function(e){
11971                 this.inKeyMode = true;
11972                 this.selectPrev();
11973             },
11974
11975             "down" : function(e){
11976                 if(!this.isExpanded()){
11977                     this.onTriggerClick();
11978                 }else{
11979                     this.inKeyMode = true;
11980                     this.selectNext();
11981                 }
11982             },
11983
11984             "enter" : function(e){
11985 //                this.onViewClick();
11986                 //return true;
11987                 this.collapse();
11988                 
11989                 if(this.fireEvent("specialkey", this, e)){
11990                     this.onViewClick(false);
11991                 }
11992                 
11993                 return true;
11994             },
11995
11996             "esc" : function(e){
11997                 this.collapse();
11998             },
11999
12000             "tab" : function(e){
12001                 this.collapse();
12002                 
12003                 if(this.fireEvent("specialkey", this, e)){
12004                     this.onViewClick(false);
12005                 }
12006                 
12007                 return true;
12008             },
12009
12010             scope : this,
12011
12012             doRelay : function(foo, bar, hname){
12013                 if(hname == 'down' || this.scope.isExpanded()){
12014                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12015                 }
12016                 return true;
12017             },
12018
12019             forceKeyDown: true
12020         });
12021         
12022         
12023         this.queryDelay = Math.max(this.queryDelay || 10,
12024                 this.mode == 'local' ? 10 : 250);
12025         
12026         
12027         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12028         
12029         if(this.typeAhead){
12030             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12031         }
12032         if(this.editable !== false){
12033             this.inputEl().on("keyup", this.onKeyUp, this);
12034         }
12035         if(this.forceSelection){
12036             this.inputEl().on('blur', this.doForce, this);
12037         }
12038         
12039         if(this.multiple){
12040             this.choices = this.el.select('ul.select2-choices', true).first();
12041             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12042         }
12043     },
12044     
12045     initTickableEvents: function()
12046     {   
12047         this.createList();
12048         
12049         if(this.hiddenName){
12050             
12051             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12052             
12053             this.hiddenField.dom.value =
12054                 this.hiddenValue !== undefined ? this.hiddenValue :
12055                 this.value !== undefined ? this.value : '';
12056
12057             // prevent input submission
12058             this.el.dom.removeAttribute('name');
12059             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12060              
12061              
12062         }
12063         
12064 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12065         
12066         this.choices = this.el.select('ul.select2-choices', true).first();
12067         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12068         if(this.triggerList){
12069             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12070         }
12071          
12072         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12073         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12074         
12075         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12076         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12077         
12078         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12079         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12080         
12081         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12082         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12083         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12084         
12085         this.okBtn.hide();
12086         this.cancelBtn.hide();
12087         
12088         var _this = this;
12089         
12090         (function(){
12091             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12092             _this.list.setWidth(lw);
12093         }).defer(100);
12094         
12095         this.list.on('mouseover', this.onViewOver, this);
12096         this.list.on('mousemove', this.onViewMove, this);
12097         
12098         this.list.on('scroll', this.onViewScroll, this);
12099         
12100         if(!this.tpl){
12101             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12102         }
12103
12104         this.view = new Roo.View(this.list, this.tpl, {
12105             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12106         });
12107         
12108         //this.view.wrapEl.setDisplayed(false);
12109         this.view.on('click', this.onViewClick, this);
12110         
12111         
12112         
12113         this.store.on('beforeload', this.onBeforeLoad, this);
12114         this.store.on('load', this.onLoad, this);
12115         this.store.on('loadexception', this.onLoadException, this);
12116         
12117         if(this.editable){
12118             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12119                 "up" : function(e){
12120                     this.inKeyMode = true;
12121                     this.selectPrev();
12122                 },
12123
12124                 "down" : function(e){
12125                     this.inKeyMode = true;
12126                     this.selectNext();
12127                 },
12128
12129                 "enter" : function(e){
12130                     if(this.fireEvent("specialkey", this, e)){
12131                         this.onViewClick(false);
12132                     }
12133                     
12134                     return true;
12135                 },
12136
12137                 "esc" : function(e){
12138                     this.onTickableFooterButtonClick(e, false, false);
12139                 },
12140
12141                 "tab" : function(e){
12142                     this.fireEvent("specialkey", this, e);
12143                     
12144                     this.onTickableFooterButtonClick(e, false, false);
12145                     
12146                     return true;
12147                 },
12148
12149                 scope : this,
12150
12151                 doRelay : function(e, fn, key){
12152                     if(this.scope.isExpanded()){
12153                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12154                     }
12155                     return true;
12156                 },
12157
12158                 forceKeyDown: true
12159             });
12160         }
12161         
12162         this.queryDelay = Math.max(this.queryDelay || 10,
12163                 this.mode == 'local' ? 10 : 250);
12164         
12165         
12166         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12167         
12168         if(this.typeAhead){
12169             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12170         }
12171         
12172         if(this.editable !== false){
12173             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12174         }
12175         
12176     },
12177
12178     onDestroy : function(){
12179         if(this.view){
12180             this.view.setStore(null);
12181             this.view.el.removeAllListeners();
12182             this.view.el.remove();
12183             this.view.purgeListeners();
12184         }
12185         if(this.list){
12186             this.list.dom.innerHTML  = '';
12187         }
12188         
12189         if(this.store){
12190             this.store.un('beforeload', this.onBeforeLoad, this);
12191             this.store.un('load', this.onLoad, this);
12192             this.store.un('loadexception', this.onLoadException, this);
12193         }
12194         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12195     },
12196
12197     // private
12198     fireKey : function(e){
12199         if(e.isNavKeyPress() && !this.list.isVisible()){
12200             this.fireEvent("specialkey", this, e);
12201         }
12202     },
12203
12204     // private
12205     onResize: function(w, h){
12206 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12207 //        
12208 //        if(typeof w != 'number'){
12209 //            // we do not handle it!?!?
12210 //            return;
12211 //        }
12212 //        var tw = this.trigger.getWidth();
12213 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12214 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12215 //        var x = w - tw;
12216 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12217 //            
12218 //        //this.trigger.setStyle('left', x+'px');
12219 //        
12220 //        if(this.list && this.listWidth === undefined){
12221 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12222 //            this.list.setWidth(lw);
12223 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12224 //        }
12225         
12226     
12227         
12228     },
12229
12230     /**
12231      * Allow or prevent the user from directly editing the field text.  If false is passed,
12232      * the user will only be able to select from the items defined in the dropdown list.  This method
12233      * is the runtime equivalent of setting the 'editable' config option at config time.
12234      * @param {Boolean} value True to allow the user to directly edit the field text
12235      */
12236     setEditable : function(value){
12237         if(value == this.editable){
12238             return;
12239         }
12240         this.editable = value;
12241         if(!value){
12242             this.inputEl().dom.setAttribute('readOnly', true);
12243             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12244             this.inputEl().addClass('x-combo-noedit');
12245         }else{
12246             this.inputEl().dom.setAttribute('readOnly', false);
12247             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12248             this.inputEl().removeClass('x-combo-noedit');
12249         }
12250     },
12251
12252     // private
12253     
12254     onBeforeLoad : function(combo,opts){
12255         if(!this.hasFocus){
12256             return;
12257         }
12258          if (!opts.add) {
12259             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12260          }
12261         this.restrictHeight();
12262         this.selectedIndex = -1;
12263     },
12264
12265     // private
12266     onLoad : function(){
12267         
12268         this.hasQuery = false;
12269         
12270         if(!this.hasFocus){
12271             return;
12272         }
12273         
12274         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12275             this.loading.hide();
12276         }
12277              
12278         if(this.store.getCount() > 0){
12279             this.expand();
12280             this.restrictHeight();
12281             if(this.lastQuery == this.allQuery){
12282                 if(this.editable && !this.tickable){
12283                     this.inputEl().dom.select();
12284                 }
12285                 
12286                 if(
12287                     !this.selectByValue(this.value, true) &&
12288                     this.autoFocus && 
12289                     (
12290                         !this.store.lastOptions ||
12291                         typeof(this.store.lastOptions.add) == 'undefined' || 
12292                         this.store.lastOptions.add != true
12293                     )
12294                 ){
12295                     this.select(0, true);
12296                 }
12297             }else{
12298                 if(this.autoFocus){
12299                     this.selectNext();
12300                 }
12301                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12302                     this.taTask.delay(this.typeAheadDelay);
12303                 }
12304             }
12305         }else{
12306             this.onEmptyResults();
12307         }
12308         
12309         //this.el.focus();
12310     },
12311     // private
12312     onLoadException : function()
12313     {
12314         this.hasQuery = false;
12315         
12316         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12317             this.loading.hide();
12318         }
12319         
12320         if(this.tickable && this.editable){
12321             return;
12322         }
12323         
12324         this.collapse();
12325         // only causes errors at present
12326         //Roo.log(this.store.reader.jsonData);
12327         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12328             // fixme
12329             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12330         //}
12331         
12332         
12333     },
12334     // private
12335     onTypeAhead : function(){
12336         if(this.store.getCount() > 0){
12337             var r = this.store.getAt(0);
12338             var newValue = r.data[this.displayField];
12339             var len = newValue.length;
12340             var selStart = this.getRawValue().length;
12341             
12342             if(selStart != len){
12343                 this.setRawValue(newValue);
12344                 this.selectText(selStart, newValue.length);
12345             }
12346         }
12347     },
12348
12349     // private
12350     onSelect : function(record, index){
12351         
12352         if(this.fireEvent('beforeselect', this, record, index) !== false){
12353         
12354             this.setFromData(index > -1 ? record.data : false);
12355             
12356             this.collapse();
12357             this.fireEvent('select', this, record, index);
12358         }
12359     },
12360
12361     /**
12362      * Returns the currently selected field value or empty string if no value is set.
12363      * @return {String} value The selected value
12364      */
12365     getValue : function(){
12366         
12367         if(this.multiple){
12368             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12369         }
12370         
12371         if(this.valueField){
12372             return typeof this.value != 'undefined' ? this.value : '';
12373         }else{
12374             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12375         }
12376     },
12377
12378     /**
12379      * Clears any text/value currently set in the field
12380      */
12381     clearValue : function(){
12382         if(this.hiddenField){
12383             this.hiddenField.dom.value = '';
12384         }
12385         this.value = '';
12386         this.setRawValue('');
12387         this.lastSelectionText = '';
12388         this.lastData = false;
12389         
12390         var close = this.closeTriggerEl();
12391         
12392         if(close){
12393             close.hide();
12394         }
12395         
12396     },
12397
12398     /**
12399      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12400      * will be displayed in the field.  If the value does not match the data value of an existing item,
12401      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12402      * Otherwise the field will be blank (although the value will still be set).
12403      * @param {String} value The value to match
12404      */
12405     setValue : function(v){
12406         if(this.multiple){
12407             this.syncValue();
12408             return;
12409         }
12410         
12411         var text = v;
12412         if(this.valueField){
12413             var r = this.findRecord(this.valueField, v);
12414             if(r){
12415                 text = r.data[this.displayField];
12416             }else if(this.valueNotFoundText !== undefined){
12417                 text = this.valueNotFoundText;
12418             }
12419         }
12420         this.lastSelectionText = text;
12421         if(this.hiddenField){
12422             this.hiddenField.dom.value = v;
12423         }
12424         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12425         this.value = v;
12426         
12427         var close = this.closeTriggerEl();
12428         
12429         if(close){
12430             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12431         }
12432     },
12433     /**
12434      * @property {Object} the last set data for the element
12435      */
12436     
12437     lastData : false,
12438     /**
12439      * Sets the value of the field based on a object which is related to the record format for the store.
12440      * @param {Object} value the value to set as. or false on reset?
12441      */
12442     setFromData : function(o){
12443         
12444         if(this.multiple){
12445             this.addItem(o);
12446             return;
12447         }
12448             
12449         var dv = ''; // display value
12450         var vv = ''; // value value..
12451         this.lastData = o;
12452         if (this.displayField) {
12453             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12454         } else {
12455             // this is an error condition!!!
12456             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12457         }
12458         
12459         if(this.valueField){
12460             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12461         }
12462         
12463         var close = this.closeTriggerEl();
12464         
12465         if(close){
12466             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12467         }
12468         
12469         if(this.hiddenField){
12470             this.hiddenField.dom.value = vv;
12471             
12472             this.lastSelectionText = dv;
12473             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12474             this.value = vv;
12475             return;
12476         }
12477         // no hidden field.. - we store the value in 'value', but still display
12478         // display field!!!!
12479         this.lastSelectionText = dv;
12480         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12481         this.value = vv;
12482         
12483         
12484         
12485     },
12486     // private
12487     reset : function(){
12488         // overridden so that last data is reset..
12489         
12490         if(this.multiple){
12491             this.clearItem();
12492             return;
12493         }
12494         
12495         this.setValue(this.originalValue);
12496         this.clearInvalid();
12497         this.lastData = false;
12498         if (this.view) {
12499             this.view.clearSelections();
12500         }
12501     },
12502     // private
12503     findRecord : function(prop, value){
12504         var record;
12505         if(this.store.getCount() > 0){
12506             this.store.each(function(r){
12507                 if(r.data[prop] == value){
12508                     record = r;
12509                     return false;
12510                 }
12511                 return true;
12512             });
12513         }
12514         return record;
12515     },
12516     
12517     getName: function()
12518     {
12519         // returns hidden if it's set..
12520         if (!this.rendered) {return ''};
12521         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12522         
12523     },
12524     // private
12525     onViewMove : function(e, t){
12526         this.inKeyMode = false;
12527     },
12528
12529     // private
12530     onViewOver : function(e, t){
12531         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12532             return;
12533         }
12534         var item = this.view.findItemFromChild(t);
12535         
12536         if(item){
12537             var index = this.view.indexOf(item);
12538             this.select(index, false);
12539         }
12540     },
12541
12542     // private
12543     onViewClick : function(view, doFocus, el, e)
12544     {
12545         var index = this.view.getSelectedIndexes()[0];
12546         
12547         var r = this.store.getAt(index);
12548         
12549         if(this.tickable){
12550             
12551             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12552                 return;
12553             }
12554             
12555             var rm = false;
12556             var _this = this;
12557             
12558             Roo.each(this.tickItems, function(v,k){
12559                 
12560                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12561                     Roo.log(v);
12562                     _this.tickItems.splice(k, 1);
12563                     
12564                     if(typeof(e) == 'undefined' && view == false){
12565                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12566                     }
12567                     
12568                     rm = true;
12569                     return;
12570                 }
12571             });
12572             
12573             if(rm){
12574                 return;
12575             }
12576             
12577             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12578                 this.tickItems.push(r.data);
12579             }
12580             
12581             if(typeof(e) == 'undefined' && view == false){
12582                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12583             }
12584                     
12585             return;
12586         }
12587         
12588         if(r){
12589             this.onSelect(r, index);
12590         }
12591         if(doFocus !== false && !this.blockFocus){
12592             this.inputEl().focus();
12593         }
12594     },
12595
12596     // private
12597     restrictHeight : function(){
12598         //this.innerList.dom.style.height = '';
12599         //var inner = this.innerList.dom;
12600         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12601         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12602         //this.list.beginUpdate();
12603         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12604         this.list.alignTo(this.inputEl(), this.listAlign);
12605         this.list.alignTo(this.inputEl(), this.listAlign);
12606         //this.list.endUpdate();
12607     },
12608
12609     // private
12610     onEmptyResults : function(){
12611         
12612         if(this.tickable && this.editable){
12613             this.restrictHeight();
12614             return;
12615         }
12616         
12617         this.collapse();
12618     },
12619
12620     /**
12621      * Returns true if the dropdown list is expanded, else false.
12622      */
12623     isExpanded : function(){
12624         return this.list.isVisible();
12625     },
12626
12627     /**
12628      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12629      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12630      * @param {String} value The data value of the item to select
12631      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12632      * selected item if it is not currently in view (defaults to true)
12633      * @return {Boolean} True if the value matched an item in the list, else false
12634      */
12635     selectByValue : function(v, scrollIntoView){
12636         if(v !== undefined && v !== null){
12637             var r = this.findRecord(this.valueField || this.displayField, v);
12638             if(r){
12639                 this.select(this.store.indexOf(r), scrollIntoView);
12640                 return true;
12641             }
12642         }
12643         return false;
12644     },
12645
12646     /**
12647      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12648      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12649      * @param {Number} index The zero-based index of the list item to select
12650      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12651      * selected item if it is not currently in view (defaults to true)
12652      */
12653     select : function(index, scrollIntoView){
12654         this.selectedIndex = index;
12655         this.view.select(index);
12656         if(scrollIntoView !== false){
12657             var el = this.view.getNode(index);
12658             /*
12659              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12660              */
12661             if(el){
12662                 this.list.scrollChildIntoView(el, false);
12663             }
12664         }
12665     },
12666
12667     // private
12668     selectNext : function(){
12669         var ct = this.store.getCount();
12670         if(ct > 0){
12671             if(this.selectedIndex == -1){
12672                 this.select(0);
12673             }else if(this.selectedIndex < ct-1){
12674                 this.select(this.selectedIndex+1);
12675             }
12676         }
12677     },
12678
12679     // private
12680     selectPrev : function(){
12681         var ct = this.store.getCount();
12682         if(ct > 0){
12683             if(this.selectedIndex == -1){
12684                 this.select(0);
12685             }else if(this.selectedIndex != 0){
12686                 this.select(this.selectedIndex-1);
12687             }
12688         }
12689     },
12690
12691     // private
12692     onKeyUp : function(e){
12693         if(this.editable !== false && !e.isSpecialKey()){
12694             this.lastKey = e.getKey();
12695             this.dqTask.delay(this.queryDelay);
12696         }
12697     },
12698
12699     // private
12700     validateBlur : function(){
12701         return !this.list || !this.list.isVisible();   
12702     },
12703
12704     // private
12705     initQuery : function(){
12706         
12707         var v = this.getRawValue();
12708         
12709         if(this.tickable && this.editable){
12710             v = this.tickableInputEl().getValue();
12711         }
12712         
12713         this.doQuery(v);
12714     },
12715
12716     // private
12717     doForce : function(){
12718         if(this.inputEl().dom.value.length > 0){
12719             this.inputEl().dom.value =
12720                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12721              
12722         }
12723     },
12724
12725     /**
12726      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12727      * query allowing the query action to be canceled if needed.
12728      * @param {String} query The SQL query to execute
12729      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12730      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12731      * saved in the current store (defaults to false)
12732      */
12733     doQuery : function(q, forceAll){
12734         
12735         if(q === undefined || q === null){
12736             q = '';
12737         }
12738         var qe = {
12739             query: q,
12740             forceAll: forceAll,
12741             combo: this,
12742             cancel:false
12743         };
12744         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12745             return false;
12746         }
12747         q = qe.query;
12748         
12749         forceAll = qe.forceAll;
12750         if(forceAll === true || (q.length >= this.minChars)){
12751             
12752             this.hasQuery = true;
12753             
12754             if(this.lastQuery != q || this.alwaysQuery){
12755                 this.lastQuery = q;
12756                 if(this.mode == 'local'){
12757                     this.selectedIndex = -1;
12758                     if(forceAll){
12759                         this.store.clearFilter();
12760                     }else{
12761                         
12762                         if(this.specialFilter){
12763                             this.fireEvent('specialfilter', this);
12764                             this.onLoad();
12765                             return;
12766                         }
12767                         
12768                         this.store.filter(this.displayField, q);
12769                     }
12770                     
12771                     this.store.fireEvent("datachanged", this.store);
12772                     
12773                     this.onLoad();
12774                     
12775                     
12776                 }else{
12777                     
12778                     this.store.baseParams[this.queryParam] = q;
12779                     
12780                     var options = {params : this.getParams(q)};
12781                     
12782                     if(this.loadNext){
12783                         options.add = true;
12784                         options.params.start = this.page * this.pageSize;
12785                     }
12786                     
12787                     this.store.load(options);
12788                     
12789                     /*
12790                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12791                      *  we should expand the list on onLoad
12792                      *  so command out it
12793                      */
12794 //                    this.expand();
12795                 }
12796             }else{
12797                 this.selectedIndex = -1;
12798                 this.onLoad();   
12799             }
12800         }
12801         
12802         this.loadNext = false;
12803     },
12804     
12805     // private
12806     getParams : function(q){
12807         var p = {};
12808         //p[this.queryParam] = q;
12809         
12810         if(this.pageSize){
12811             p.start = 0;
12812             p.limit = this.pageSize;
12813         }
12814         return p;
12815     },
12816
12817     /**
12818      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12819      */
12820     collapse : function(){
12821         if(!this.isExpanded()){
12822             return;
12823         }
12824         
12825         this.list.hide();
12826         
12827         if(this.tickable){
12828             this.hasFocus = false;
12829             this.okBtn.hide();
12830             this.cancelBtn.hide();
12831             this.trigger.show();
12832             
12833             if(this.editable){
12834                 this.tickableInputEl().dom.value = '';
12835                 this.tickableInputEl().blur();
12836             }
12837             
12838         }
12839         
12840         Roo.get(document).un('mousedown', this.collapseIf, this);
12841         Roo.get(document).un('mousewheel', this.collapseIf, this);
12842         if (!this.editable) {
12843             Roo.get(document).un('keydown', this.listKeyPress, this);
12844         }
12845         this.fireEvent('collapse', this);
12846     },
12847
12848     // private
12849     collapseIf : function(e){
12850         var in_combo  = e.within(this.el);
12851         var in_list =  e.within(this.list);
12852         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12853         
12854         if (in_combo || in_list || is_list) {
12855             //e.stopPropagation();
12856             return;
12857         }
12858         
12859         if(this.tickable){
12860             this.onTickableFooterButtonClick(e, false, false);
12861         }
12862
12863         this.collapse();
12864         
12865     },
12866
12867     /**
12868      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12869      */
12870     expand : function(){
12871        
12872         if(this.isExpanded() || !this.hasFocus){
12873             return;
12874         }
12875         
12876         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12877         this.list.setWidth(lw);
12878         
12879         
12880          Roo.log('expand');
12881         
12882         this.list.show();
12883         
12884         this.restrictHeight();
12885         
12886         if(this.tickable){
12887             
12888             this.tickItems = Roo.apply([], this.item);
12889             
12890             this.okBtn.show();
12891             this.cancelBtn.show();
12892             this.trigger.hide();
12893             
12894             if(this.editable){
12895                 this.tickableInputEl().focus();
12896             }
12897             
12898         }
12899         
12900         Roo.get(document).on('mousedown', this.collapseIf, this);
12901         Roo.get(document).on('mousewheel', this.collapseIf, this);
12902         if (!this.editable) {
12903             Roo.get(document).on('keydown', this.listKeyPress, this);
12904         }
12905         
12906         this.fireEvent('expand', this);
12907     },
12908
12909     // private
12910     // Implements the default empty TriggerField.onTriggerClick function
12911     onTriggerClick : function(e)
12912     {
12913         Roo.log('trigger click');
12914         
12915         if(this.disabled || !this.triggerList){
12916             return;
12917         }
12918         
12919         this.page = 0;
12920         this.loadNext = false;
12921         
12922         if(this.isExpanded()){
12923             this.collapse();
12924             if (!this.blockFocus) {
12925                 this.inputEl().focus();
12926             }
12927             
12928         }else {
12929             this.hasFocus = true;
12930             if(this.triggerAction == 'all') {
12931                 this.doQuery(this.allQuery, true);
12932             } else {
12933                 this.doQuery(this.getRawValue());
12934             }
12935             if (!this.blockFocus) {
12936                 this.inputEl().focus();
12937             }
12938         }
12939     },
12940     
12941     onTickableTriggerClick : function(e)
12942     {
12943         if(this.disabled){
12944             return;
12945         }
12946         
12947         this.page = 0;
12948         this.loadNext = false;
12949         this.hasFocus = true;
12950         
12951         if(this.triggerAction == 'all') {
12952             this.doQuery(this.allQuery, true);
12953         } else {
12954             this.doQuery(this.getRawValue());
12955         }
12956     },
12957     
12958     onSearchFieldClick : function(e)
12959     {
12960         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12961             this.onTickableFooterButtonClick(e, false, false);
12962             return;
12963         }
12964         
12965         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12966             return;
12967         }
12968         
12969         this.page = 0;
12970         this.loadNext = false;
12971         this.hasFocus = true;
12972         
12973         if(this.triggerAction == 'all') {
12974             this.doQuery(this.allQuery, true);
12975         } else {
12976             this.doQuery(this.getRawValue());
12977         }
12978     },
12979     
12980     listKeyPress : function(e)
12981     {
12982         //Roo.log('listkeypress');
12983         // scroll to first matching element based on key pres..
12984         if (e.isSpecialKey()) {
12985             return false;
12986         }
12987         var k = String.fromCharCode(e.getKey()).toUpperCase();
12988         //Roo.log(k);
12989         var match  = false;
12990         var csel = this.view.getSelectedNodes();
12991         var cselitem = false;
12992         if (csel.length) {
12993             var ix = this.view.indexOf(csel[0]);
12994             cselitem  = this.store.getAt(ix);
12995             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12996                 cselitem = false;
12997             }
12998             
12999         }
13000         
13001         this.store.each(function(v) { 
13002             if (cselitem) {
13003                 // start at existing selection.
13004                 if (cselitem.id == v.id) {
13005                     cselitem = false;
13006                 }
13007                 return true;
13008             }
13009                 
13010             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13011                 match = this.store.indexOf(v);
13012                 return false;
13013             }
13014             return true;
13015         }, this);
13016         
13017         if (match === false) {
13018             return true; // no more action?
13019         }
13020         // scroll to?
13021         this.view.select(match);
13022         var sn = Roo.get(this.view.getSelectedNodes()[0])
13023         sn.scrollIntoView(sn.dom.parentNode, false);
13024     },
13025     
13026     onViewScroll : function(e, t){
13027         
13028         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13029             return;
13030         }
13031         
13032         this.hasQuery = true;
13033         
13034         this.loading = this.list.select('.loading', true).first();
13035         
13036         if(this.loading === null){
13037             this.list.createChild({
13038                 tag: 'div',
13039                 cls: 'loading select2-more-results select2-active',
13040                 html: 'Loading more results...'
13041             })
13042             
13043             this.loading = this.list.select('.loading', true).first();
13044             
13045             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13046             
13047             this.loading.hide();
13048         }
13049         
13050         this.loading.show();
13051         
13052         var _combo = this;
13053         
13054         this.page++;
13055         this.loadNext = true;
13056         
13057         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13058         
13059         return;
13060     },
13061     
13062     addItem : function(o)
13063     {   
13064         var dv = ''; // display value
13065         
13066         if (this.displayField) {
13067             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13068         } else {
13069             // this is an error condition!!!
13070             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13071         }
13072         
13073         if(!dv.length){
13074             return;
13075         }
13076         
13077         var choice = this.choices.createChild({
13078             tag: 'li',
13079             cls: 'select2-search-choice',
13080             cn: [
13081                 {
13082                     tag: 'div',
13083                     html: dv
13084                 },
13085                 {
13086                     tag: 'a',
13087                     href: '#',
13088                     cls: 'select2-search-choice-close',
13089                     tabindex: '-1'
13090                 }
13091             ]
13092             
13093         }, this.searchField);
13094         
13095         var close = choice.select('a.select2-search-choice-close', true).first()
13096         
13097         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13098         
13099         this.item.push(o);
13100         
13101         this.lastData = o;
13102         
13103         this.syncValue();
13104         
13105         this.inputEl().dom.value = '';
13106         
13107         this.validate();
13108     },
13109     
13110     onRemoveItem : function(e, _self, o)
13111     {
13112         e.preventDefault();
13113         
13114         this.lastItem = Roo.apply([], this.item);
13115         
13116         var index = this.item.indexOf(o.data) * 1;
13117         
13118         if( index < 0){
13119             Roo.log('not this item?!');
13120             return;
13121         }
13122         
13123         this.item.splice(index, 1);
13124         o.item.remove();
13125         
13126         this.syncValue();
13127         
13128         this.fireEvent('remove', this, e);
13129         
13130         this.validate();
13131         
13132     },
13133     
13134     syncValue : function()
13135     {
13136         if(!this.item.length){
13137             this.clearValue();
13138             return;
13139         }
13140             
13141         var value = [];
13142         var _this = this;
13143         Roo.each(this.item, function(i){
13144             if(_this.valueField){
13145                 value.push(i[_this.valueField]);
13146                 return;
13147             }
13148
13149             value.push(i);
13150         });
13151
13152         this.value = value.join(',');
13153
13154         if(this.hiddenField){
13155             this.hiddenField.dom.value = this.value;
13156         }
13157         
13158         this.store.fireEvent("datachanged", this.store);
13159     },
13160     
13161     clearItem : function()
13162     {
13163         if(!this.multiple){
13164             return;
13165         }
13166         
13167         this.item = [];
13168         
13169         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13170            c.remove();
13171         });
13172         
13173         this.syncValue();
13174         
13175         this.validate();
13176         
13177         if(this.tickable && !Roo.isTouch){
13178             this.view.refresh();
13179         }
13180     },
13181     
13182     inputEl: function ()
13183     {
13184         if(Roo.isTouch && this.mobileTouchView){
13185             return this.el.select('input.form-control',true).first();
13186         }
13187         
13188         if(this.tickable){
13189             return this.searchField;
13190         }
13191         
13192         return this.el.select('input.form-control',true).first();
13193     },
13194     
13195     
13196     onTickableFooterButtonClick : function(e, btn, el)
13197     {
13198         e.preventDefault();
13199         
13200         this.lastItem = Roo.apply([], this.item);
13201         
13202         if(btn && btn.name == 'cancel'){
13203             this.tickItems = Roo.apply([], this.item);
13204             this.collapse();
13205             return;
13206         }
13207         
13208         this.clearItem();
13209         
13210         var _this = this;
13211         
13212         Roo.each(this.tickItems, function(o){
13213             _this.addItem(o);
13214         });
13215         
13216         this.collapse();
13217         
13218     },
13219     
13220     validate : function()
13221     {
13222         var v = this.getRawValue();
13223         
13224         if(this.multiple){
13225             v = this.getValue();
13226         }
13227         
13228         if(this.disabled || this.allowBlank || v.length){
13229             this.markValid();
13230             return true;
13231         }
13232         
13233         this.markInvalid();
13234         return false;
13235     },
13236     
13237     tickableInputEl : function()
13238     {
13239         if(!this.tickable || !this.editable){
13240             return this.inputEl();
13241         }
13242         
13243         return this.inputEl().select('.select2-search-field-input', true).first();
13244     },
13245     
13246     
13247     getAutoCreateTouchView : function()
13248     {
13249         var id = Roo.id();
13250         
13251         var cfg = {
13252             cls: 'form-group' //input-group
13253         };
13254         
13255         var input =  {
13256             tag: 'input',
13257             id : id,
13258             type : this.inputType,
13259             cls : 'form-control x-combo-noedit',
13260             autocomplete: 'new-password',
13261             placeholder : this.placeholder || '',
13262             readonly : true
13263         };
13264         
13265         if (this.name) {
13266             input.name = this.name;
13267         }
13268         
13269         if (this.size) {
13270             input.cls += ' input-' + this.size;
13271         }
13272         
13273         if (this.disabled) {
13274             input.disabled = true;
13275         }
13276         
13277         var inputblock = {
13278             cls : '',
13279             cn : [
13280                 input
13281             ]
13282         };
13283         
13284         if(this.before){
13285             inputblock.cls += ' input-group';
13286             
13287             inputblock.cn.unshift({
13288                 tag :'span',
13289                 cls : 'input-group-addon',
13290                 html : this.before
13291             });
13292         }
13293         
13294         if(this.removable && !this.multiple){
13295             inputblock.cls += ' roo-removable';
13296             
13297             inputblock.cn.push({
13298                 tag: 'button',
13299                 html : 'x',
13300                 cls : 'roo-combo-removable-btn close'
13301             });
13302         }
13303
13304         if(this.hasFeedback && !this.allowBlank){
13305             
13306             inputblock.cls += ' has-feedback';
13307             
13308             inputblock.cn.push({
13309                 tag: 'span',
13310                 cls: 'glyphicon form-control-feedback'
13311             });
13312             
13313         }
13314         
13315         if (this.after) {
13316             
13317             inputblock.cls += (this.before) ? '' : ' input-group';
13318             
13319             inputblock.cn.push({
13320                 tag :'span',
13321                 cls : 'input-group-addon',
13322                 html : this.after
13323             });
13324         }
13325
13326         var box = {
13327             tag: 'div',
13328             cn: [
13329                 {
13330                     tag: 'input',
13331                     type : 'hidden',
13332                     cls: 'form-hidden-field'
13333                 },
13334                 inputblock
13335             ]
13336             
13337         };
13338         
13339         if(this.multiple){
13340             box = {
13341                 tag: 'div',
13342                 cn: [
13343                     {
13344                         tag: 'input',
13345                         type : 'hidden',
13346                         cls: 'form-hidden-field'
13347                     },
13348                     {
13349                         tag: 'ul',
13350                         cls: 'select2-choices',
13351                         cn:[
13352                             {
13353                                 tag: 'li',
13354                                 cls: 'select2-search-field',
13355                                 cn: [
13356
13357                                     inputblock
13358                                 ]
13359                             }
13360                         ]
13361                     }
13362                 ]
13363             }
13364         };
13365         
13366         var combobox = {
13367             cls: 'select2-container input-group',
13368             cn: [
13369                 box
13370             ]
13371         };
13372         
13373         if(this.multiple){
13374             combobox.cls += ' select2-container-multi';
13375         }
13376         
13377         var align = this.labelAlign || this.parentLabelAlign();
13378         
13379         cfg.cn = combobox;
13380         
13381         if(this.fieldLabel.length){
13382             
13383             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13384             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13385             
13386             cfg.cn = [
13387                 {
13388                     tag: 'label',
13389                     cls : 'control-label ' + lw,
13390                     html : this.fieldLabel
13391
13392                 },
13393                 {
13394                     cls : cw, 
13395                     cn: [
13396                         combobox
13397                     ]
13398                 }
13399             ];
13400         }
13401         
13402         var settings = this;
13403         
13404         ['xs','sm','md','lg'].map(function(size){
13405             if (settings[size]) {
13406                 cfg.cls += ' col-' + size + '-' + settings[size];
13407             }
13408         });
13409         
13410         return cfg;
13411     },
13412     
13413     initTouchView : function()
13414     {
13415         this.renderTouchView();
13416         
13417         this.touchViewEl.on('scroll', function(){
13418             this.el.dom.scrollTop = 0;
13419         }, this);
13420         
13421         this.inputEl().on("click", this.showTouchView, this);
13422         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13423         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13424         
13425         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13426         
13427         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13428         this.store.on('load', this.onTouchViewLoad, this);
13429         this.store.on('loadexception', this.onTouchViewLoadException, this);
13430         
13431         if(this.hiddenName){
13432             
13433             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13434             
13435             this.hiddenField.dom.value =
13436                 this.hiddenValue !== undefined ? this.hiddenValue :
13437                 this.value !== undefined ? this.value : '';
13438         
13439             this.el.dom.removeAttribute('name');
13440             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13441         }
13442         
13443         if(this.multiple){
13444             this.choices = this.el.select('ul.select2-choices', true).first();
13445             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13446         }
13447         
13448         if(this.removable && !this.multiple){
13449             var close = this.closeTriggerEl();
13450             if(close){
13451                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13452                 close.on('click', this.removeBtnClick, this, close);
13453             }
13454         }
13455         
13456         return;
13457         
13458         
13459     },
13460     
13461     renderTouchView : function()
13462     {
13463         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13464         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13465         
13466         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13467         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13468         
13469         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13470         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13471         this.touchViewBodyEl.setStyle('overflow', 'auto');
13472         
13473         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13474         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13475         
13476         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13477         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13478         
13479     },
13480     
13481     showTouchView : function()
13482     {
13483         this.touchViewHeaderEl.hide();
13484
13485         if(this.fieldLabel.length){
13486             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13487             this.touchViewHeaderEl.show();
13488         }
13489
13490         this.touchViewEl.show();
13491
13492         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13493         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13494
13495         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13496
13497         if(this.fieldLabel.length){
13498             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13499         }
13500         
13501         this.touchViewBodyEl.setHeight(bodyHeight);
13502
13503         if(this.animate){
13504             var _this = this;
13505             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13506         }else{
13507             this.touchViewEl.addClass('in');
13508         }
13509
13510         this.doTouchViewQuery();
13511         
13512     },
13513     
13514     hideTouchView : function()
13515     {
13516         this.touchViewEl.removeClass('in');
13517
13518         if(this.animate){
13519             var _this = this;
13520             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13521         }else{
13522             this.touchViewEl.setStyle('display', 'none');
13523         }
13524         
13525     },
13526     
13527     setTouchViewValue : function()
13528     {
13529         if(this.multiple){
13530             this.clearItem();
13531         
13532             var _this = this;
13533
13534             Roo.each(this.tickItems, function(o){
13535                 this.addItem(o);
13536             }, this);
13537         }
13538         
13539         this.hideTouchView();
13540     },
13541     
13542     doTouchViewQuery : function()
13543     {
13544         var qe = {
13545             query: '',
13546             forceAll: true,
13547             combo: this,
13548             cancel:false
13549         };
13550         
13551         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13552             return false;
13553         }
13554         
13555         if(!this.alwaysQuery || this.mode == 'local'){
13556             this.onTouchViewLoad();
13557             return;
13558         }
13559         
13560         this.store.load();
13561     },
13562     
13563     onTouchViewBeforeLoad : function(combo,opts)
13564     {
13565         return;
13566     },
13567
13568     // private
13569     onTouchViewLoad : function()
13570     {
13571         if(this.store.getCount() < 1){
13572             this.onTouchViewEmptyResults();
13573             return;
13574         }
13575         
13576         this.clearTouchView();
13577         
13578         var rawValue = this.getRawValue();
13579         
13580         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13581         
13582         this.tickItems = [];
13583         
13584         this.store.data.each(function(d, rowIndex){
13585             var row = this.touchViewListGroup.createChild(template);
13586             
13587             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13588                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13589             }
13590             
13591             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13592                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13593             }
13594             
13595             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13596                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13597                 this.tickItems.push(d.data);
13598             }
13599             
13600             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13601             
13602         }, this);
13603         
13604         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13605         
13606         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13607
13608         if(this.fieldLabel.length){
13609             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13610         }
13611
13612         var listHeight = this.touchViewListGroup.getHeight();
13613         
13614         var _this = this;
13615         
13616         if(firstChecked && listHeight > bodyHeight){
13617             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13618         }
13619         
13620     },
13621     
13622     onTouchViewLoadException : function()
13623     {
13624         this.hideTouchView();
13625     },
13626     
13627     onTouchViewEmptyResults : function()
13628     {
13629         this.clearTouchView();
13630         
13631         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13632         
13633         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13634         
13635     },
13636     
13637     clearTouchView : function()
13638     {
13639         this.touchViewListGroup.dom.innerHTML = '';
13640     },
13641     
13642     onTouchViewClick : function(e, el, o)
13643     {
13644         e.preventDefault();
13645         
13646         var row = o.row;
13647         var rowIndex = o.rowIndex;
13648         
13649         var r = this.store.getAt(rowIndex);
13650         
13651         if(!this.multiple){
13652             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13653                 c.dom.removeAttribute('checked');
13654             }, this);
13655             
13656             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13657         
13658             this.setFromData(r.data);
13659             
13660             var close = this.closeTriggerEl();
13661         
13662             if(close){
13663                 close.show();
13664             }
13665
13666             this.hideTouchView();
13667             
13668             this.fireEvent('select', this, r, rowIndex);
13669             
13670             return;
13671         }
13672         
13673         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13674             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13675             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13676             return;
13677         }
13678         
13679         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13680         this.addItem(r.data);
13681         this.tickItems.push(r.data);
13682         
13683     }
13684     
13685
13686     /** 
13687     * @cfg {Boolean} grow 
13688     * @hide 
13689     */
13690     /** 
13691     * @cfg {Number} growMin 
13692     * @hide 
13693     */
13694     /** 
13695     * @cfg {Number} growMax 
13696     * @hide 
13697     */
13698     /**
13699      * @hide
13700      * @method autoSize
13701      */
13702 });
13703
13704 Roo.apply(Roo.bootstrap.ComboBox,  {
13705     
13706     header : {
13707         tag: 'div',
13708         cls: 'modal-header',
13709         cn: [
13710             {
13711                 tag: 'h4',
13712                 cls: 'modal-title'
13713             }
13714         ]
13715     },
13716     
13717     body : {
13718         tag: 'div',
13719         cls: 'modal-body',
13720         cn: [
13721             {
13722                 tag: 'ul',
13723                 cls: 'list-group'
13724             }
13725         ]
13726     },
13727     
13728     listItemRadio : {
13729         tag: 'li',
13730         cls: 'list-group-item',
13731         cn: [
13732             {
13733                 tag: 'span',
13734                 cls: 'roo-combobox-list-group-item-value'
13735             },
13736             {
13737                 tag: 'div',
13738                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13739                 cn: [
13740                     {
13741                         tag: 'input',
13742                         type: 'radio'
13743                     },
13744                     {
13745                         tag: 'label'
13746                     }
13747                 ]
13748             }
13749         ]
13750     },
13751     
13752     listItemCheckbox : {
13753         tag: 'li',
13754         cls: 'list-group-item',
13755         cn: [
13756             {
13757                 tag: 'span',
13758                 cls: 'roo-combobox-list-group-item-value'
13759             },
13760             {
13761                 tag: 'div',
13762                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13763                 cn: [
13764                     {
13765                         tag: 'input',
13766                         type: 'checkbox'
13767                     },
13768                     {
13769                         tag: 'label'
13770                     }
13771                 ]
13772             }
13773         ]
13774     },
13775     
13776     emptyResult : {
13777         tag: 'div',
13778         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13779     },
13780     
13781     footer : {
13782         tag: 'div',
13783         cls: 'modal-footer',
13784         cn: [
13785             {
13786                 tag: 'div',
13787                 cls: 'row',
13788                 cn: [
13789                     {
13790                         tag: 'div',
13791                         cls: 'col-xs-6 text-left',
13792                         cn: {
13793                             tag: 'button',
13794                             cls: 'btn btn-danger roo-touch-view-cancel',
13795                             html: 'Cancel'
13796                         }
13797                     },
13798                     {
13799                         tag: 'div',
13800                         cls: 'col-xs-6 text-right',
13801                         cn: {
13802                             tag: 'button',
13803                             cls: 'btn btn-success roo-touch-view-ok',
13804                             html: 'OK'
13805                         }
13806                     }
13807                 ]
13808             }
13809         ]
13810         
13811     }
13812 });
13813
13814 Roo.apply(Roo.bootstrap.ComboBox,  {
13815     
13816     touchViewTemplate : {
13817         tag: 'div',
13818         cls: 'modal fade roo-combobox-touch-view',
13819         cn: [
13820             {
13821                 tag: 'div',
13822                 cls: 'modal-dialog',
13823                 cn: [
13824                     {
13825                         tag: 'div',
13826                         cls: 'modal-content',
13827                         cn: [
13828                             Roo.bootstrap.ComboBox.header,
13829                             Roo.bootstrap.ComboBox.body,
13830                             Roo.bootstrap.ComboBox.footer
13831                         ]
13832                     }
13833                 ]
13834             }
13835         ]
13836     }
13837 });/*
13838  * Based on:
13839  * Ext JS Library 1.1.1
13840  * Copyright(c) 2006-2007, Ext JS, LLC.
13841  *
13842  * Originally Released Under LGPL - original licence link has changed is not relivant.
13843  *
13844  * Fork - LGPL
13845  * <script type="text/javascript">
13846  */
13847
13848 /**
13849  * @class Roo.View
13850  * @extends Roo.util.Observable
13851  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13852  * This class also supports single and multi selection modes. <br>
13853  * Create a data model bound view:
13854  <pre><code>
13855  var store = new Roo.data.Store(...);
13856
13857  var view = new Roo.View({
13858     el : "my-element",
13859     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13860  
13861     singleSelect: true,
13862     selectedClass: "ydataview-selected",
13863     store: store
13864  });
13865
13866  // listen for node click?
13867  view.on("click", function(vw, index, node, e){
13868  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13869  });
13870
13871  // load XML data
13872  dataModel.load("foobar.xml");
13873  </code></pre>
13874  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13875  * <br><br>
13876  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13877  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13878  * 
13879  * Note: old style constructor is still suported (container, template, config)
13880  * 
13881  * @constructor
13882  * Create a new View
13883  * @param {Object} config The config object
13884  * 
13885  */
13886 Roo.View = function(config, depreciated_tpl, depreciated_config){
13887     
13888     this.parent = false;
13889     
13890     if (typeof(depreciated_tpl) == 'undefined') {
13891         // new way.. - universal constructor.
13892         Roo.apply(this, config);
13893         this.el  = Roo.get(this.el);
13894     } else {
13895         // old format..
13896         this.el  = Roo.get(config);
13897         this.tpl = depreciated_tpl;
13898         Roo.apply(this, depreciated_config);
13899     }
13900     this.wrapEl  = this.el.wrap().wrap();
13901     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13902     
13903     
13904     if(typeof(this.tpl) == "string"){
13905         this.tpl = new Roo.Template(this.tpl);
13906     } else {
13907         // support xtype ctors..
13908         this.tpl = new Roo.factory(this.tpl, Roo);
13909     }
13910     
13911     
13912     this.tpl.compile();
13913     
13914     /** @private */
13915     this.addEvents({
13916         /**
13917          * @event beforeclick
13918          * Fires before a click is processed. Returns false to cancel the default action.
13919          * @param {Roo.View} this
13920          * @param {Number} index The index of the target node
13921          * @param {HTMLElement} node The target node
13922          * @param {Roo.EventObject} e The raw event object
13923          */
13924             "beforeclick" : true,
13925         /**
13926          * @event click
13927          * Fires when a template node is clicked.
13928          * @param {Roo.View} this
13929          * @param {Number} index The index of the target node
13930          * @param {HTMLElement} node The target node
13931          * @param {Roo.EventObject} e The raw event object
13932          */
13933             "click" : true,
13934         /**
13935          * @event dblclick
13936          * Fires when a template node is double clicked.
13937          * @param {Roo.View} this
13938          * @param {Number} index The index of the target node
13939          * @param {HTMLElement} node The target node
13940          * @param {Roo.EventObject} e The raw event object
13941          */
13942             "dblclick" : true,
13943         /**
13944          * @event contextmenu
13945          * Fires when a template node is right clicked.
13946          * @param {Roo.View} this
13947          * @param {Number} index The index of the target node
13948          * @param {HTMLElement} node The target node
13949          * @param {Roo.EventObject} e The raw event object
13950          */
13951             "contextmenu" : true,
13952         /**
13953          * @event selectionchange
13954          * Fires when the selected nodes change.
13955          * @param {Roo.View} this
13956          * @param {Array} selections Array of the selected nodes
13957          */
13958             "selectionchange" : true,
13959     
13960         /**
13961          * @event beforeselect
13962          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13963          * @param {Roo.View} this
13964          * @param {HTMLElement} node The node to be selected
13965          * @param {Array} selections Array of currently selected nodes
13966          */
13967             "beforeselect" : true,
13968         /**
13969          * @event preparedata
13970          * Fires on every row to render, to allow you to change the data.
13971          * @param {Roo.View} this
13972          * @param {Object} data to be rendered (change this)
13973          */
13974           "preparedata" : true
13975           
13976           
13977         });
13978
13979
13980
13981     this.el.on({
13982         "click": this.onClick,
13983         "dblclick": this.onDblClick,
13984         "contextmenu": this.onContextMenu,
13985         scope:this
13986     });
13987
13988     this.selections = [];
13989     this.nodes = [];
13990     this.cmp = new Roo.CompositeElementLite([]);
13991     if(this.store){
13992         this.store = Roo.factory(this.store, Roo.data);
13993         this.setStore(this.store, true);
13994     }
13995     
13996     if ( this.footer && this.footer.xtype) {
13997            
13998          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13999         
14000         this.footer.dataSource = this.store;
14001         this.footer.container = fctr;
14002         this.footer = Roo.factory(this.footer, Roo);
14003         fctr.insertFirst(this.el);
14004         
14005         // this is a bit insane - as the paging toolbar seems to detach the el..
14006 //        dom.parentNode.parentNode.parentNode
14007          // they get detached?
14008     }
14009     
14010     
14011     Roo.View.superclass.constructor.call(this);
14012     
14013     
14014 };
14015
14016 Roo.extend(Roo.View, Roo.util.Observable, {
14017     
14018      /**
14019      * @cfg {Roo.data.Store} store Data store to load data from.
14020      */
14021     store : false,
14022     
14023     /**
14024      * @cfg {String|Roo.Element} el The container element.
14025      */
14026     el : '',
14027     
14028     /**
14029      * @cfg {String|Roo.Template} tpl The template used by this View 
14030      */
14031     tpl : false,
14032     /**
14033      * @cfg {String} dataName the named area of the template to use as the data area
14034      *                          Works with domtemplates roo-name="name"
14035      */
14036     dataName: false,
14037     /**
14038      * @cfg {String} selectedClass The css class to add to selected nodes
14039      */
14040     selectedClass : "x-view-selected",
14041      /**
14042      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14043      */
14044     emptyText : "",
14045     
14046     /**
14047      * @cfg {String} text to display on mask (default Loading)
14048      */
14049     mask : false,
14050     /**
14051      * @cfg {Boolean} multiSelect Allow multiple selection
14052      */
14053     multiSelect : false,
14054     /**
14055      * @cfg {Boolean} singleSelect Allow single selection
14056      */
14057     singleSelect:  false,
14058     
14059     /**
14060      * @cfg {Boolean} toggleSelect - selecting 
14061      */
14062     toggleSelect : false,
14063     
14064     /**
14065      * @cfg {Boolean} tickable - selecting 
14066      */
14067     tickable : false,
14068     
14069     /**
14070      * Returns the element this view is bound to.
14071      * @return {Roo.Element}
14072      */
14073     getEl : function(){
14074         return this.wrapEl;
14075     },
14076     
14077     
14078
14079     /**
14080      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14081      */
14082     refresh : function(){
14083         //Roo.log('refresh');
14084         var t = this.tpl;
14085         
14086         // if we are using something like 'domtemplate', then
14087         // the what gets used is:
14088         // t.applySubtemplate(NAME, data, wrapping data..)
14089         // the outer template then get' applied with
14090         //     the store 'extra data'
14091         // and the body get's added to the
14092         //      roo-name="data" node?
14093         //      <span class='roo-tpl-{name}'></span> ?????
14094         
14095         
14096         
14097         this.clearSelections();
14098         this.el.update("");
14099         var html = [];
14100         var records = this.store.getRange();
14101         if(records.length < 1) {
14102             
14103             // is this valid??  = should it render a template??
14104             
14105             this.el.update(this.emptyText);
14106             return;
14107         }
14108         var el = this.el;
14109         if (this.dataName) {
14110             this.el.update(t.apply(this.store.meta)); //????
14111             el = this.el.child('.roo-tpl-' + this.dataName);
14112         }
14113         
14114         for(var i = 0, len = records.length; i < len; i++){
14115             var data = this.prepareData(records[i].data, i, records[i]);
14116             this.fireEvent("preparedata", this, data, i, records[i]);
14117             
14118             var d = Roo.apply({}, data);
14119             
14120             if(this.tickable){
14121                 Roo.apply(d, {'roo-id' : Roo.id()});
14122                 
14123                 var _this = this;
14124             
14125                 Roo.each(this.parent.item, function(item){
14126                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14127                         return;
14128                     }
14129                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14130                 });
14131             }
14132             
14133             html[html.length] = Roo.util.Format.trim(
14134                 this.dataName ?
14135                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14136                     t.apply(d)
14137             );
14138         }
14139         
14140         
14141         
14142         el.update(html.join(""));
14143         this.nodes = el.dom.childNodes;
14144         this.updateIndexes(0);
14145     },
14146     
14147
14148     /**
14149      * Function to override to reformat the data that is sent to
14150      * the template for each node.
14151      * DEPRICATED - use the preparedata event handler.
14152      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14153      * a JSON object for an UpdateManager bound view).
14154      */
14155     prepareData : function(data, index, record)
14156     {
14157         this.fireEvent("preparedata", this, data, index, record);
14158         return data;
14159     },
14160
14161     onUpdate : function(ds, record){
14162         // Roo.log('on update');   
14163         this.clearSelections();
14164         var index = this.store.indexOf(record);
14165         var n = this.nodes[index];
14166         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14167         n.parentNode.removeChild(n);
14168         this.updateIndexes(index, index);
14169     },
14170
14171     
14172     
14173 // --------- FIXME     
14174     onAdd : function(ds, records, index)
14175     {
14176         //Roo.log(['on Add', ds, records, index] );        
14177         this.clearSelections();
14178         if(this.nodes.length == 0){
14179             this.refresh();
14180             return;
14181         }
14182         var n = this.nodes[index];
14183         for(var i = 0, len = records.length; i < len; i++){
14184             var d = this.prepareData(records[i].data, i, records[i]);
14185             if(n){
14186                 this.tpl.insertBefore(n, d);
14187             }else{
14188                 
14189                 this.tpl.append(this.el, d);
14190             }
14191         }
14192         this.updateIndexes(index);
14193     },
14194
14195     onRemove : function(ds, record, index){
14196        // Roo.log('onRemove');
14197         this.clearSelections();
14198         var el = this.dataName  ?
14199             this.el.child('.roo-tpl-' + this.dataName) :
14200             this.el; 
14201         
14202         el.dom.removeChild(this.nodes[index]);
14203         this.updateIndexes(index);
14204     },
14205
14206     /**
14207      * Refresh an individual node.
14208      * @param {Number} index
14209      */
14210     refreshNode : function(index){
14211         this.onUpdate(this.store, this.store.getAt(index));
14212     },
14213
14214     updateIndexes : function(startIndex, endIndex){
14215         var ns = this.nodes;
14216         startIndex = startIndex || 0;
14217         endIndex = endIndex || ns.length - 1;
14218         for(var i = startIndex; i <= endIndex; i++){
14219             ns[i].nodeIndex = i;
14220         }
14221     },
14222
14223     /**
14224      * Changes the data store this view uses and refresh the view.
14225      * @param {Store} store
14226      */
14227     setStore : function(store, initial){
14228         if(!initial && this.store){
14229             this.store.un("datachanged", this.refresh);
14230             this.store.un("add", this.onAdd);
14231             this.store.un("remove", this.onRemove);
14232             this.store.un("update", this.onUpdate);
14233             this.store.un("clear", this.refresh);
14234             this.store.un("beforeload", this.onBeforeLoad);
14235             this.store.un("load", this.onLoad);
14236             this.store.un("loadexception", this.onLoad);
14237         }
14238         if(store){
14239           
14240             store.on("datachanged", this.refresh, this);
14241             store.on("add", this.onAdd, this);
14242             store.on("remove", this.onRemove, this);
14243             store.on("update", this.onUpdate, this);
14244             store.on("clear", this.refresh, this);
14245             store.on("beforeload", this.onBeforeLoad, this);
14246             store.on("load", this.onLoad, this);
14247             store.on("loadexception", this.onLoad, this);
14248         }
14249         
14250         if(store){
14251             this.refresh();
14252         }
14253     },
14254     /**
14255      * onbeforeLoad - masks the loading area.
14256      *
14257      */
14258     onBeforeLoad : function(store,opts)
14259     {
14260          //Roo.log('onBeforeLoad');   
14261         if (!opts.add) {
14262             this.el.update("");
14263         }
14264         this.el.mask(this.mask ? this.mask : "Loading" ); 
14265     },
14266     onLoad : function ()
14267     {
14268         this.el.unmask();
14269     },
14270     
14271
14272     /**
14273      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14274      * @param {HTMLElement} node
14275      * @return {HTMLElement} The template node
14276      */
14277     findItemFromChild : function(node){
14278         var el = this.dataName  ?
14279             this.el.child('.roo-tpl-' + this.dataName,true) :
14280             this.el.dom; 
14281         
14282         if(!node || node.parentNode == el){
14283                     return node;
14284             }
14285             var p = node.parentNode;
14286             while(p && p != el){
14287             if(p.parentNode == el){
14288                 return p;
14289             }
14290             p = p.parentNode;
14291         }
14292             return null;
14293     },
14294
14295     /** @ignore */
14296     onClick : function(e){
14297         var item = this.findItemFromChild(e.getTarget());
14298         if(item){
14299             var index = this.indexOf(item);
14300             if(this.onItemClick(item, index, e) !== false){
14301                 this.fireEvent("click", this, index, item, e);
14302             }
14303         }else{
14304             this.clearSelections();
14305         }
14306     },
14307
14308     /** @ignore */
14309     onContextMenu : function(e){
14310         var item = this.findItemFromChild(e.getTarget());
14311         if(item){
14312             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14313         }
14314     },
14315
14316     /** @ignore */
14317     onDblClick : function(e){
14318         var item = this.findItemFromChild(e.getTarget());
14319         if(item){
14320             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14321         }
14322     },
14323
14324     onItemClick : function(item, index, e)
14325     {
14326         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14327             return false;
14328         }
14329         if (this.toggleSelect) {
14330             var m = this.isSelected(item) ? 'unselect' : 'select';
14331             //Roo.log(m);
14332             var _t = this;
14333             _t[m](item, true, false);
14334             return true;
14335         }
14336         if(this.multiSelect || this.singleSelect){
14337             if(this.multiSelect && e.shiftKey && this.lastSelection){
14338                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14339             }else{
14340                 this.select(item, this.multiSelect && e.ctrlKey);
14341                 this.lastSelection = item;
14342             }
14343             
14344             if(!this.tickable){
14345                 e.preventDefault();
14346             }
14347             
14348         }
14349         return true;
14350     },
14351
14352     /**
14353      * Get the number of selected nodes.
14354      * @return {Number}
14355      */
14356     getSelectionCount : function(){
14357         return this.selections.length;
14358     },
14359
14360     /**
14361      * Get the currently selected nodes.
14362      * @return {Array} An array of HTMLElements
14363      */
14364     getSelectedNodes : function(){
14365         return this.selections;
14366     },
14367
14368     /**
14369      * Get the indexes of the selected nodes.
14370      * @return {Array}
14371      */
14372     getSelectedIndexes : function(){
14373         var indexes = [], s = this.selections;
14374         for(var i = 0, len = s.length; i < len; i++){
14375             indexes.push(s[i].nodeIndex);
14376         }
14377         return indexes;
14378     },
14379
14380     /**
14381      * Clear all selections
14382      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14383      */
14384     clearSelections : function(suppressEvent){
14385         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14386             this.cmp.elements = this.selections;
14387             this.cmp.removeClass(this.selectedClass);
14388             this.selections = [];
14389             if(!suppressEvent){
14390                 this.fireEvent("selectionchange", this, this.selections);
14391             }
14392         }
14393     },
14394
14395     /**
14396      * Returns true if the passed node is selected
14397      * @param {HTMLElement/Number} node The node or node index
14398      * @return {Boolean}
14399      */
14400     isSelected : function(node){
14401         var s = this.selections;
14402         if(s.length < 1){
14403             return false;
14404         }
14405         node = this.getNode(node);
14406         return s.indexOf(node) !== -1;
14407     },
14408
14409     /**
14410      * Selects nodes.
14411      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14412      * @param {Boolean} keepExisting (optional) true to keep existing selections
14413      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14414      */
14415     select : function(nodeInfo, keepExisting, suppressEvent){
14416         if(nodeInfo instanceof Array){
14417             if(!keepExisting){
14418                 this.clearSelections(true);
14419             }
14420             for(var i = 0, len = nodeInfo.length; i < len; i++){
14421                 this.select(nodeInfo[i], true, true);
14422             }
14423             return;
14424         } 
14425         var node = this.getNode(nodeInfo);
14426         if(!node || this.isSelected(node)){
14427             return; // already selected.
14428         }
14429         if(!keepExisting){
14430             this.clearSelections(true);
14431         }
14432         
14433         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14434             Roo.fly(node).addClass(this.selectedClass);
14435             this.selections.push(node);
14436             if(!suppressEvent){
14437                 this.fireEvent("selectionchange", this, this.selections);
14438             }
14439         }
14440         
14441         
14442     },
14443       /**
14444      * Unselects nodes.
14445      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14446      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14447      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14448      */
14449     unselect : function(nodeInfo, keepExisting, suppressEvent)
14450     {
14451         if(nodeInfo instanceof Array){
14452             Roo.each(this.selections, function(s) {
14453                 this.unselect(s, nodeInfo);
14454             }, this);
14455             return;
14456         }
14457         var node = this.getNode(nodeInfo);
14458         if(!node || !this.isSelected(node)){
14459             //Roo.log("not selected");
14460             return; // not selected.
14461         }
14462         // fireevent???
14463         var ns = [];
14464         Roo.each(this.selections, function(s) {
14465             if (s == node ) {
14466                 Roo.fly(node).removeClass(this.selectedClass);
14467
14468                 return;
14469             }
14470             ns.push(s);
14471         },this);
14472         
14473         this.selections= ns;
14474         this.fireEvent("selectionchange", this, this.selections);
14475     },
14476
14477     /**
14478      * Gets a template node.
14479      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14480      * @return {HTMLElement} The node or null if it wasn't found
14481      */
14482     getNode : function(nodeInfo){
14483         if(typeof nodeInfo == "string"){
14484             return document.getElementById(nodeInfo);
14485         }else if(typeof nodeInfo == "number"){
14486             return this.nodes[nodeInfo];
14487         }
14488         return nodeInfo;
14489     },
14490
14491     /**
14492      * Gets a range template nodes.
14493      * @param {Number} startIndex
14494      * @param {Number} endIndex
14495      * @return {Array} An array of nodes
14496      */
14497     getNodes : function(start, end){
14498         var ns = this.nodes;
14499         start = start || 0;
14500         end = typeof end == "undefined" ? ns.length - 1 : end;
14501         var nodes = [];
14502         if(start <= end){
14503             for(var i = start; i <= end; i++){
14504                 nodes.push(ns[i]);
14505             }
14506         } else{
14507             for(var i = start; i >= end; i--){
14508                 nodes.push(ns[i]);
14509             }
14510         }
14511         return nodes;
14512     },
14513
14514     /**
14515      * Finds the index of the passed node
14516      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14517      * @return {Number} The index of the node or -1
14518      */
14519     indexOf : function(node){
14520         node = this.getNode(node);
14521         if(typeof node.nodeIndex == "number"){
14522             return node.nodeIndex;
14523         }
14524         var ns = this.nodes;
14525         for(var i = 0, len = ns.length; i < len; i++){
14526             if(ns[i] == node){
14527                 return i;
14528             }
14529         }
14530         return -1;
14531     }
14532 });
14533 /*
14534  * - LGPL
14535  *
14536  * based on jquery fullcalendar
14537  * 
14538  */
14539
14540 Roo.bootstrap = Roo.bootstrap || {};
14541 /**
14542  * @class Roo.bootstrap.Calendar
14543  * @extends Roo.bootstrap.Component
14544  * Bootstrap Calendar class
14545  * @cfg {Boolean} loadMask (true|false) default false
14546  * @cfg {Object} header generate the user specific header of the calendar, default false
14547
14548  * @constructor
14549  * Create a new Container
14550  * @param {Object} config The config object
14551  */
14552
14553
14554
14555 Roo.bootstrap.Calendar = function(config){
14556     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14557      this.addEvents({
14558         /**
14559              * @event select
14560              * Fires when a date is selected
14561              * @param {DatePicker} this
14562              * @param {Date} date The selected date
14563              */
14564         'select': true,
14565         /**
14566              * @event monthchange
14567              * Fires when the displayed month changes 
14568              * @param {DatePicker} this
14569              * @param {Date} date The selected month
14570              */
14571         'monthchange': true,
14572         /**
14573              * @event evententer
14574              * Fires when mouse over an event
14575              * @param {Calendar} this
14576              * @param {event} Event
14577              */
14578         'evententer': true,
14579         /**
14580              * @event eventleave
14581              * Fires when the mouse leaves an
14582              * @param {Calendar} this
14583              * @param {event}
14584              */
14585         'eventleave': true,
14586         /**
14587              * @event eventclick
14588              * Fires when the mouse click an
14589              * @param {Calendar} this
14590              * @param {event}
14591              */
14592         'eventclick': true
14593         
14594     });
14595
14596 };
14597
14598 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14599     
14600      /**
14601      * @cfg {Number} startDay
14602      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14603      */
14604     startDay : 0,
14605     
14606     loadMask : false,
14607     
14608     header : false,
14609       
14610     getAutoCreate : function(){
14611         
14612         
14613         var fc_button = function(name, corner, style, content ) {
14614             return Roo.apply({},{
14615                 tag : 'span',
14616                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14617                          (corner.length ?
14618                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14619                             ''
14620                         ),
14621                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14622                 unselectable: 'on'
14623             });
14624         };
14625         
14626         var header = {};
14627         
14628         if(!this.header){
14629             header = {
14630                 tag : 'table',
14631                 cls : 'fc-header',
14632                 style : 'width:100%',
14633                 cn : [
14634                     {
14635                         tag: 'tr',
14636                         cn : [
14637                             {
14638                                 tag : 'td',
14639                                 cls : 'fc-header-left',
14640                                 cn : [
14641                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14642                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14643                                     { tag: 'span', cls: 'fc-header-space' },
14644                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14645
14646
14647                                 ]
14648                             },
14649
14650                             {
14651                                 tag : 'td',
14652                                 cls : 'fc-header-center',
14653                                 cn : [
14654                                     {
14655                                         tag: 'span',
14656                                         cls: 'fc-header-title',
14657                                         cn : {
14658                                             tag: 'H2',
14659                                             html : 'month / year'
14660                                         }
14661                                     }
14662
14663                                 ]
14664                             },
14665                             {
14666                                 tag : 'td',
14667                                 cls : 'fc-header-right',
14668                                 cn : [
14669                               /*      fc_button('month', 'left', '', 'month' ),
14670                                     fc_button('week', '', '', 'week' ),
14671                                     fc_button('day', 'right', '', 'day' )
14672                                 */    
14673
14674                                 ]
14675                             }
14676
14677                         ]
14678                     }
14679                 ]
14680             };
14681         }
14682         
14683         header = this.header;
14684         
14685        
14686         var cal_heads = function() {
14687             var ret = [];
14688             // fixme - handle this.
14689             
14690             for (var i =0; i < Date.dayNames.length; i++) {
14691                 var d = Date.dayNames[i];
14692                 ret.push({
14693                     tag: 'th',
14694                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14695                     html : d.substring(0,3)
14696                 });
14697                 
14698             }
14699             ret[0].cls += ' fc-first';
14700             ret[6].cls += ' fc-last';
14701             return ret;
14702         };
14703         var cal_cell = function(n) {
14704             return  {
14705                 tag: 'td',
14706                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14707                 cn : [
14708                     {
14709                         cn : [
14710                             {
14711                                 cls: 'fc-day-number',
14712                                 html: 'D'
14713                             },
14714                             {
14715                                 cls: 'fc-day-content',
14716                              
14717                                 cn : [
14718                                      {
14719                                         style: 'position: relative;' // height: 17px;
14720                                     }
14721                                 ]
14722                             }
14723                             
14724                             
14725                         ]
14726                     }
14727                 ]
14728                 
14729             }
14730         };
14731         var cal_rows = function() {
14732             
14733             var ret = [];
14734             for (var r = 0; r < 6; r++) {
14735                 var row= {
14736                     tag : 'tr',
14737                     cls : 'fc-week',
14738                     cn : []
14739                 };
14740                 
14741                 for (var i =0; i < Date.dayNames.length; i++) {
14742                     var d = Date.dayNames[i];
14743                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14744
14745                 }
14746                 row.cn[0].cls+=' fc-first';
14747                 row.cn[0].cn[0].style = 'min-height:90px';
14748                 row.cn[6].cls+=' fc-last';
14749                 ret.push(row);
14750                 
14751             }
14752             ret[0].cls += ' fc-first';
14753             ret[4].cls += ' fc-prev-last';
14754             ret[5].cls += ' fc-last';
14755             return ret;
14756             
14757         };
14758         
14759         var cal_table = {
14760             tag: 'table',
14761             cls: 'fc-border-separate',
14762             style : 'width:100%',
14763             cellspacing  : 0,
14764             cn : [
14765                 { 
14766                     tag: 'thead',
14767                     cn : [
14768                         { 
14769                             tag: 'tr',
14770                             cls : 'fc-first fc-last',
14771                             cn : cal_heads()
14772                         }
14773                     ]
14774                 },
14775                 { 
14776                     tag: 'tbody',
14777                     cn : cal_rows()
14778                 }
14779                   
14780             ]
14781         };
14782          
14783          var cfg = {
14784             cls : 'fc fc-ltr',
14785             cn : [
14786                 header,
14787                 {
14788                     cls : 'fc-content',
14789                     style : "position: relative;",
14790                     cn : [
14791                         {
14792                             cls : 'fc-view fc-view-month fc-grid',
14793                             style : 'position: relative',
14794                             unselectable : 'on',
14795                             cn : [
14796                                 {
14797                                     cls : 'fc-event-container',
14798                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14799                                 },
14800                                 cal_table
14801                             ]
14802                         }
14803                     ]
14804     
14805                 }
14806            ] 
14807             
14808         };
14809         
14810          
14811         
14812         return cfg;
14813     },
14814     
14815     
14816     initEvents : function()
14817     {
14818         if(!this.store){
14819             throw "can not find store for calendar";
14820         }
14821         
14822         var mark = {
14823             tag: "div",
14824             cls:"x-dlg-mask",
14825             style: "text-align:center",
14826             cn: [
14827                 {
14828                     tag: "div",
14829                     style: "background-color:white;width:50%;margin:250 auto",
14830                     cn: [
14831                         {
14832                             tag: "img",
14833                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14834                         },
14835                         {
14836                             tag: "span",
14837                             html: "Loading"
14838                         }
14839                         
14840                     ]
14841                 }
14842             ]
14843         };
14844         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14845         
14846         var size = this.el.select('.fc-content', true).first().getSize();
14847         this.maskEl.setSize(size.width, size.height);
14848         this.maskEl.enableDisplayMode("block");
14849         if(!this.loadMask){
14850             this.maskEl.hide();
14851         }
14852         
14853         this.store = Roo.factory(this.store, Roo.data);
14854         this.store.on('load', this.onLoad, this);
14855         this.store.on('beforeload', this.onBeforeLoad, this);
14856         
14857         this.resize();
14858         
14859         this.cells = this.el.select('.fc-day',true);
14860         //Roo.log(this.cells);
14861         this.textNodes = this.el.query('.fc-day-number');
14862         this.cells.addClassOnOver('fc-state-hover');
14863         
14864         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14865         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14866         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14867         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14868         
14869         this.on('monthchange', this.onMonthChange, this);
14870         
14871         this.update(new Date().clearTime());
14872     },
14873     
14874     resize : function() {
14875         var sz  = this.el.getSize();
14876         
14877         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14878         this.el.select('.fc-day-content div',true).setHeight(34);
14879     },
14880     
14881     
14882     // private
14883     showPrevMonth : function(e){
14884         this.update(this.activeDate.add("mo", -1));
14885     },
14886     showToday : function(e){
14887         this.update(new Date().clearTime());
14888     },
14889     // private
14890     showNextMonth : function(e){
14891         this.update(this.activeDate.add("mo", 1));
14892     },
14893
14894     // private
14895     showPrevYear : function(){
14896         this.update(this.activeDate.add("y", -1));
14897     },
14898
14899     // private
14900     showNextYear : function(){
14901         this.update(this.activeDate.add("y", 1));
14902     },
14903
14904     
14905    // private
14906     update : function(date)
14907     {
14908         var vd = this.activeDate;
14909         this.activeDate = date;
14910 //        if(vd && this.el){
14911 //            var t = date.getTime();
14912 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14913 //                Roo.log('using add remove');
14914 //                
14915 //                this.fireEvent('monthchange', this, date);
14916 //                
14917 //                this.cells.removeClass("fc-state-highlight");
14918 //                this.cells.each(function(c){
14919 //                   if(c.dateValue == t){
14920 //                       c.addClass("fc-state-highlight");
14921 //                       setTimeout(function(){
14922 //                            try{c.dom.firstChild.focus();}catch(e){}
14923 //                       }, 50);
14924 //                       return false;
14925 //                   }
14926 //                   return true;
14927 //                });
14928 //                return;
14929 //            }
14930 //        }
14931         
14932         var days = date.getDaysInMonth();
14933         
14934         var firstOfMonth = date.getFirstDateOfMonth();
14935         var startingPos = firstOfMonth.getDay()-this.startDay;
14936         
14937         if(startingPos < this.startDay){
14938             startingPos += 7;
14939         }
14940         
14941         var pm = date.add(Date.MONTH, -1);
14942         var prevStart = pm.getDaysInMonth()-startingPos;
14943 //        
14944         this.cells = this.el.select('.fc-day',true);
14945         this.textNodes = this.el.query('.fc-day-number');
14946         this.cells.addClassOnOver('fc-state-hover');
14947         
14948         var cells = this.cells.elements;
14949         var textEls = this.textNodes;
14950         
14951         Roo.each(cells, function(cell){
14952             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14953         });
14954         
14955         days += startingPos;
14956
14957         // convert everything to numbers so it's fast
14958         var day = 86400000;
14959         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14960         //Roo.log(d);
14961         //Roo.log(pm);
14962         //Roo.log(prevStart);
14963         
14964         var today = new Date().clearTime().getTime();
14965         var sel = date.clearTime().getTime();
14966         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14967         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14968         var ddMatch = this.disabledDatesRE;
14969         var ddText = this.disabledDatesText;
14970         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14971         var ddaysText = this.disabledDaysText;
14972         var format = this.format;
14973         
14974         var setCellClass = function(cal, cell){
14975             cell.row = 0;
14976             cell.events = [];
14977             cell.more = [];
14978             //Roo.log('set Cell Class');
14979             cell.title = "";
14980             var t = d.getTime();
14981             
14982             //Roo.log(d);
14983             
14984             cell.dateValue = t;
14985             if(t == today){
14986                 cell.className += " fc-today";
14987                 cell.className += " fc-state-highlight";
14988                 cell.title = cal.todayText;
14989             }
14990             if(t == sel){
14991                 // disable highlight in other month..
14992                 //cell.className += " fc-state-highlight";
14993                 
14994             }
14995             // disabling
14996             if(t < min) {
14997                 cell.className = " fc-state-disabled";
14998                 cell.title = cal.minText;
14999                 return;
15000             }
15001             if(t > max) {
15002                 cell.className = " fc-state-disabled";
15003                 cell.title = cal.maxText;
15004                 return;
15005             }
15006             if(ddays){
15007                 if(ddays.indexOf(d.getDay()) != -1){
15008                     cell.title = ddaysText;
15009                     cell.className = " fc-state-disabled";
15010                 }
15011             }
15012             if(ddMatch && format){
15013                 var fvalue = d.dateFormat(format);
15014                 if(ddMatch.test(fvalue)){
15015                     cell.title = ddText.replace("%0", fvalue);
15016                     cell.className = " fc-state-disabled";
15017                 }
15018             }
15019             
15020             if (!cell.initialClassName) {
15021                 cell.initialClassName = cell.dom.className;
15022             }
15023             
15024             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15025         };
15026
15027         var i = 0;
15028         
15029         for(; i < startingPos; i++) {
15030             textEls[i].innerHTML = (++prevStart);
15031             d.setDate(d.getDate()+1);
15032             
15033             cells[i].className = "fc-past fc-other-month";
15034             setCellClass(this, cells[i]);
15035         }
15036         
15037         var intDay = 0;
15038         
15039         for(; i < days; i++){
15040             intDay = i - startingPos + 1;
15041             textEls[i].innerHTML = (intDay);
15042             d.setDate(d.getDate()+1);
15043             
15044             cells[i].className = ''; // "x-date-active";
15045             setCellClass(this, cells[i]);
15046         }
15047         var extraDays = 0;
15048         
15049         for(; i < 42; i++) {
15050             textEls[i].innerHTML = (++extraDays);
15051             d.setDate(d.getDate()+1);
15052             
15053             cells[i].className = "fc-future fc-other-month";
15054             setCellClass(this, cells[i]);
15055         }
15056         
15057         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15058         
15059         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15060         
15061         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15062         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15063         
15064         if(totalRows != 6){
15065             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15066             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15067         }
15068         
15069         this.fireEvent('monthchange', this, date);
15070         
15071         
15072         /*
15073         if(!this.internalRender){
15074             var main = this.el.dom.firstChild;
15075             var w = main.offsetWidth;
15076             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15077             Roo.fly(main).setWidth(w);
15078             this.internalRender = true;
15079             // opera does not respect the auto grow header center column
15080             // then, after it gets a width opera refuses to recalculate
15081             // without a second pass
15082             if(Roo.isOpera && !this.secondPass){
15083                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15084                 this.secondPass = true;
15085                 this.update.defer(10, this, [date]);
15086             }
15087         }
15088         */
15089         
15090     },
15091     
15092     findCell : function(dt) {
15093         dt = dt.clearTime().getTime();
15094         var ret = false;
15095         this.cells.each(function(c){
15096             //Roo.log("check " +c.dateValue + '?=' + dt);
15097             if(c.dateValue == dt){
15098                 ret = c;
15099                 return false;
15100             }
15101             return true;
15102         });
15103         
15104         return ret;
15105     },
15106     
15107     findCells : function(ev) {
15108         var s = ev.start.clone().clearTime().getTime();
15109        // Roo.log(s);
15110         var e= ev.end.clone().clearTime().getTime();
15111        // Roo.log(e);
15112         var ret = [];
15113         this.cells.each(function(c){
15114              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15115             
15116             if(c.dateValue > e){
15117                 return ;
15118             }
15119             if(c.dateValue < s){
15120                 return ;
15121             }
15122             ret.push(c);
15123         });
15124         
15125         return ret;    
15126     },
15127     
15128 //    findBestRow: function(cells)
15129 //    {
15130 //        var ret = 0;
15131 //        
15132 //        for (var i =0 ; i < cells.length;i++) {
15133 //            ret  = Math.max(cells[i].rows || 0,ret);
15134 //        }
15135 //        return ret;
15136 //        
15137 //    },
15138     
15139     
15140     addItem : function(ev)
15141     {
15142         // look for vertical location slot in
15143         var cells = this.findCells(ev);
15144         
15145 //        ev.row = this.findBestRow(cells);
15146         
15147         // work out the location.
15148         
15149         var crow = false;
15150         var rows = [];
15151         for(var i =0; i < cells.length; i++) {
15152             
15153             cells[i].row = cells[0].row;
15154             
15155             if(i == 0){
15156                 cells[i].row = cells[i].row + 1;
15157             }
15158             
15159             if (!crow) {
15160                 crow = {
15161                     start : cells[i],
15162                     end :  cells[i]
15163                 };
15164                 continue;
15165             }
15166             if (crow.start.getY() == cells[i].getY()) {
15167                 // on same row.
15168                 crow.end = cells[i];
15169                 continue;
15170             }
15171             // different row.
15172             rows.push(crow);
15173             crow = {
15174                 start: cells[i],
15175                 end : cells[i]
15176             };
15177             
15178         }
15179         
15180         rows.push(crow);
15181         ev.els = [];
15182         ev.rows = rows;
15183         ev.cells = cells;
15184         
15185         cells[0].events.push(ev);
15186         
15187         this.calevents.push(ev);
15188     },
15189     
15190     clearEvents: function() {
15191         
15192         if(!this.calevents){
15193             return;
15194         }
15195         
15196         Roo.each(this.cells.elements, function(c){
15197             c.row = 0;
15198             c.events = [];
15199             c.more = [];
15200         });
15201         
15202         Roo.each(this.calevents, function(e) {
15203             Roo.each(e.els, function(el) {
15204                 el.un('mouseenter' ,this.onEventEnter, this);
15205                 el.un('mouseleave' ,this.onEventLeave, this);
15206                 el.remove();
15207             },this);
15208         },this);
15209         
15210         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15211             e.remove();
15212         });
15213         
15214     },
15215     
15216     renderEvents: function()
15217     {   
15218         var _this = this;
15219         
15220         this.cells.each(function(c) {
15221             
15222             if(c.row < 5){
15223                 return;
15224             }
15225             
15226             var ev = c.events;
15227             
15228             var r = 4;
15229             if(c.row != c.events.length){
15230                 r = 4 - (4 - (c.row - c.events.length));
15231             }
15232             
15233             c.events = ev.slice(0, r);
15234             c.more = ev.slice(r);
15235             
15236             if(c.more.length && c.more.length == 1){
15237                 c.events.push(c.more.pop());
15238             }
15239             
15240             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15241             
15242         });
15243             
15244         this.cells.each(function(c) {
15245             
15246             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15247             
15248             
15249             for (var e = 0; e < c.events.length; e++){
15250                 var ev = c.events[e];
15251                 var rows = ev.rows;
15252                 
15253                 for(var i = 0; i < rows.length; i++) {
15254                 
15255                     // how many rows should it span..
15256
15257                     var  cfg = {
15258                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15259                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15260
15261                         unselectable : "on",
15262                         cn : [
15263                             {
15264                                 cls: 'fc-event-inner',
15265                                 cn : [
15266     //                                {
15267     //                                  tag:'span',
15268     //                                  cls: 'fc-event-time',
15269     //                                  html : cells.length > 1 ? '' : ev.time
15270     //                                },
15271                                     {
15272                                       tag:'span',
15273                                       cls: 'fc-event-title',
15274                                       html : String.format('{0}', ev.title)
15275                                     }
15276
15277
15278                                 ]
15279                             },
15280                             {
15281                                 cls: 'ui-resizable-handle ui-resizable-e',
15282                                 html : '&nbsp;&nbsp;&nbsp'
15283                             }
15284
15285                         ]
15286                     };
15287
15288                     if (i == 0) {
15289                         cfg.cls += ' fc-event-start';
15290                     }
15291                     if ((i+1) == rows.length) {
15292                         cfg.cls += ' fc-event-end';
15293                     }
15294
15295                     var ctr = _this.el.select('.fc-event-container',true).first();
15296                     var cg = ctr.createChild(cfg);
15297
15298                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15299                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15300
15301                     var r = (c.more.length) ? 1 : 0;
15302                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15303                     cg.setWidth(ebox.right - sbox.x -2);
15304
15305                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15306                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15307                     cg.on('click', _this.onEventClick, _this, ev);
15308
15309                     ev.els.push(cg);
15310                     
15311                 }
15312                 
15313             }
15314             
15315             
15316             if(c.more.length){
15317                 var  cfg = {
15318                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15319                     style : 'position: absolute',
15320                     unselectable : "on",
15321                     cn : [
15322                         {
15323                             cls: 'fc-event-inner',
15324                             cn : [
15325                                 {
15326                                   tag:'span',
15327                                   cls: 'fc-event-title',
15328                                   html : 'More'
15329                                 }
15330
15331
15332                             ]
15333                         },
15334                         {
15335                             cls: 'ui-resizable-handle ui-resizable-e',
15336                             html : '&nbsp;&nbsp;&nbsp'
15337                         }
15338
15339                     ]
15340                 };
15341
15342                 var ctr = _this.el.select('.fc-event-container',true).first();
15343                 var cg = ctr.createChild(cfg);
15344
15345                 var sbox = c.select('.fc-day-content',true).first().getBox();
15346                 var ebox = c.select('.fc-day-content',true).first().getBox();
15347                 //Roo.log(cg);
15348                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15349                 cg.setWidth(ebox.right - sbox.x -2);
15350
15351                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15352                 
15353             }
15354             
15355         });
15356         
15357         
15358         
15359     },
15360     
15361     onEventEnter: function (e, el,event,d) {
15362         this.fireEvent('evententer', this, el, event);
15363     },
15364     
15365     onEventLeave: function (e, el,event,d) {
15366         this.fireEvent('eventleave', this, el, event);
15367     },
15368     
15369     onEventClick: function (e, el,event,d) {
15370         this.fireEvent('eventclick', this, el, event);
15371     },
15372     
15373     onMonthChange: function () {
15374         this.store.load();
15375     },
15376     
15377     onMoreEventClick: function(e, el, more)
15378     {
15379         var _this = this;
15380         
15381         this.calpopover.placement = 'right';
15382         this.calpopover.setTitle('More');
15383         
15384         this.calpopover.setContent('');
15385         
15386         var ctr = this.calpopover.el.select('.popover-content', true).first();
15387         
15388         Roo.each(more, function(m){
15389             var cfg = {
15390                 cls : 'fc-event-hori fc-event-draggable',
15391                 html : m.title
15392             };
15393             var cg = ctr.createChild(cfg);
15394             
15395             cg.on('click', _this.onEventClick, _this, m);
15396         });
15397         
15398         this.calpopover.show(el);
15399         
15400         
15401     },
15402     
15403     onLoad: function () 
15404     {   
15405         this.calevents = [];
15406         var cal = this;
15407         
15408         if(this.store.getCount() > 0){
15409             this.store.data.each(function(d){
15410                cal.addItem({
15411                     id : d.data.id,
15412                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15413                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15414                     time : d.data.start_time,
15415                     title : d.data.title,
15416                     description : d.data.description,
15417                     venue : d.data.venue
15418                 });
15419             });
15420         }
15421         
15422         this.renderEvents();
15423         
15424         if(this.calevents.length && this.loadMask){
15425             this.maskEl.hide();
15426         }
15427     },
15428     
15429     onBeforeLoad: function()
15430     {
15431         this.clearEvents();
15432         if(this.loadMask){
15433             this.maskEl.show();
15434         }
15435     }
15436 });
15437
15438  
15439  /*
15440  * - LGPL
15441  *
15442  * element
15443  * 
15444  */
15445
15446 /**
15447  * @class Roo.bootstrap.Popover
15448  * @extends Roo.bootstrap.Component
15449  * Bootstrap Popover class
15450  * @cfg {String} html contents of the popover   (or false to use children..)
15451  * @cfg {String} title of popover (or false to hide)
15452  * @cfg {String} placement how it is placed
15453  * @cfg {String} trigger click || hover (or false to trigger manually)
15454  * @cfg {String} over what (parent or false to trigger manually.)
15455  * @cfg {Number} delay - delay before showing
15456  
15457  * @constructor
15458  * Create a new Popover
15459  * @param {Object} config The config object
15460  */
15461
15462 Roo.bootstrap.Popover = function(config){
15463     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15464 };
15465
15466 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15467     
15468     title: 'Fill in a title',
15469     html: false,
15470     
15471     placement : 'right',
15472     trigger : 'hover', // hover
15473     
15474     delay : 0,
15475     
15476     over: 'parent',
15477     
15478     can_build_overlaid : false,
15479     
15480     getChildContainer : function()
15481     {
15482         return this.el.select('.popover-content',true).first();
15483     },
15484     
15485     getAutoCreate : function(){
15486          Roo.log('make popover?');
15487         var cfg = {
15488            cls : 'popover roo-dynamic',
15489            style: 'display:block',
15490            cn : [
15491                 {
15492                     cls : 'arrow'
15493                 },
15494                 {
15495                     cls : 'popover-inner',
15496                     cn : [
15497                         {
15498                             tag: 'h3',
15499                             cls: 'popover-title',
15500                             html : this.title
15501                         },
15502                         {
15503                             cls : 'popover-content',
15504                             html : this.html
15505                         }
15506                     ]
15507                     
15508                 }
15509            ]
15510         };
15511         
15512         return cfg;
15513     },
15514     setTitle: function(str)
15515     {
15516         this.title = str;
15517         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15518     },
15519     setContent: function(str)
15520     {
15521         this.html = str;
15522         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15523     },
15524     // as it get's added to the bottom of the page.
15525     onRender : function(ct, position)
15526     {
15527         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15528         if(!this.el){
15529             var cfg = Roo.apply({},  this.getAutoCreate());
15530             cfg.id = Roo.id();
15531             
15532             if (this.cls) {
15533                 cfg.cls += ' ' + this.cls;
15534             }
15535             if (this.style) {
15536                 cfg.style = this.style;
15537             }
15538             Roo.log("adding to ")
15539             this.el = Roo.get(document.body).createChild(cfg, position);
15540             Roo.log(this.el);
15541         }
15542         this.initEvents();
15543     },
15544     
15545     initEvents : function()
15546     {
15547         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15548         this.el.enableDisplayMode('block');
15549         this.el.hide();
15550         if (this.over === false) {
15551             return; 
15552         }
15553         if (this.triggers === false) {
15554             return;
15555         }
15556         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15557         var triggers = this.trigger ? this.trigger.split(' ') : [];
15558         Roo.each(triggers, function(trigger) {
15559         
15560             if (trigger == 'click') {
15561                 on_el.on('click', this.toggle, this);
15562             } else if (trigger != 'manual') {
15563                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15564                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15565       
15566                 on_el.on(eventIn  ,this.enter, this);
15567                 on_el.on(eventOut, this.leave, this);
15568             }
15569         }, this);
15570         
15571     },
15572     
15573     
15574     // private
15575     timeout : null,
15576     hoverState : null,
15577     
15578     toggle : function () {
15579         this.hoverState == 'in' ? this.leave() : this.enter();
15580     },
15581     
15582     enter : function () {
15583        
15584     
15585         clearTimeout(this.timeout);
15586     
15587         this.hoverState = 'in';
15588     
15589         if (!this.delay || !this.delay.show) {
15590             this.show();
15591             return;
15592         }
15593         var _t = this;
15594         this.timeout = setTimeout(function () {
15595             if (_t.hoverState == 'in') {
15596                 _t.show();
15597             }
15598         }, this.delay.show)
15599     },
15600     leave : function() {
15601         clearTimeout(this.timeout);
15602     
15603         this.hoverState = 'out';
15604     
15605         if (!this.delay || !this.delay.hide) {
15606             this.hide();
15607             return;
15608         }
15609         var _t = this;
15610         this.timeout = setTimeout(function () {
15611             if (_t.hoverState == 'out') {
15612                 _t.hide();
15613             }
15614         }, this.delay.hide)
15615     },
15616     
15617     show : function (on_el)
15618     {
15619         if (!on_el) {
15620             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15621         }
15622         // set content.
15623         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15624         if (this.html !== false) {
15625             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15626         }
15627         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15628         if (!this.title.length) {
15629             this.el.select('.popover-title',true).hide();
15630         }
15631         
15632         var placement = typeof this.placement == 'function' ?
15633             this.placement.call(this, this.el, on_el) :
15634             this.placement;
15635             
15636         var autoToken = /\s?auto?\s?/i;
15637         var autoPlace = autoToken.test(placement);
15638         if (autoPlace) {
15639             placement = placement.replace(autoToken, '') || 'top';
15640         }
15641         
15642         //this.el.detach()
15643         //this.el.setXY([0,0]);
15644         this.el.show();
15645         this.el.dom.style.display='block';
15646         this.el.addClass(placement);
15647         
15648         //this.el.appendTo(on_el);
15649         
15650         var p = this.getPosition();
15651         var box = this.el.getBox();
15652         
15653         if (autoPlace) {
15654             // fixme..
15655         }
15656         var align = Roo.bootstrap.Popover.alignment[placement];
15657         this.el.alignTo(on_el, align[0],align[1]);
15658         //var arrow = this.el.select('.arrow',true).first();
15659         //arrow.set(align[2], 
15660         
15661         this.el.addClass('in');
15662         
15663         
15664         if (this.el.hasClass('fade')) {
15665             // fade it?
15666         }
15667         
15668     },
15669     hide : function()
15670     {
15671         this.el.setXY([0,0]);
15672         this.el.removeClass('in');
15673         this.el.hide();
15674         this.hoverState = null;
15675         
15676     }
15677     
15678 });
15679
15680 Roo.bootstrap.Popover.alignment = {
15681     'left' : ['r-l', [-10,0], 'right'],
15682     'right' : ['l-r', [10,0], 'left'],
15683     'bottom' : ['t-b', [0,10], 'top'],
15684     'top' : [ 'b-t', [0,-10], 'bottom']
15685 };
15686
15687  /*
15688  * - LGPL
15689  *
15690  * Progress
15691  * 
15692  */
15693
15694 /**
15695  * @class Roo.bootstrap.Progress
15696  * @extends Roo.bootstrap.Component
15697  * Bootstrap Progress class
15698  * @cfg {Boolean} striped striped of the progress bar
15699  * @cfg {Boolean} active animated of the progress bar
15700  * 
15701  * 
15702  * @constructor
15703  * Create a new Progress
15704  * @param {Object} config The config object
15705  */
15706
15707 Roo.bootstrap.Progress = function(config){
15708     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15709 };
15710
15711 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15712     
15713     striped : false,
15714     active: false,
15715     
15716     getAutoCreate : function(){
15717         var cfg = {
15718             tag: 'div',
15719             cls: 'progress'
15720         };
15721         
15722         
15723         if(this.striped){
15724             cfg.cls += ' progress-striped';
15725         }
15726       
15727         if(this.active){
15728             cfg.cls += ' active';
15729         }
15730         
15731         
15732         return cfg;
15733     }
15734    
15735 });
15736
15737  
15738
15739  /*
15740  * - LGPL
15741  *
15742  * ProgressBar
15743  * 
15744  */
15745
15746 /**
15747  * @class Roo.bootstrap.ProgressBar
15748  * @extends Roo.bootstrap.Component
15749  * Bootstrap ProgressBar class
15750  * @cfg {Number} aria_valuenow aria-value now
15751  * @cfg {Number} aria_valuemin aria-value min
15752  * @cfg {Number} aria_valuemax aria-value max
15753  * @cfg {String} label label for the progress bar
15754  * @cfg {String} panel (success | info | warning | danger )
15755  * @cfg {String} role role of the progress bar
15756  * @cfg {String} sr_only text
15757  * 
15758  * 
15759  * @constructor
15760  * Create a new ProgressBar
15761  * @param {Object} config The config object
15762  */
15763
15764 Roo.bootstrap.ProgressBar = function(config){
15765     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15766 };
15767
15768 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15769     
15770     aria_valuenow : 0,
15771     aria_valuemin : 0,
15772     aria_valuemax : 100,
15773     label : false,
15774     panel : false,
15775     role : false,
15776     sr_only: false,
15777     
15778     getAutoCreate : function()
15779     {
15780         
15781         var cfg = {
15782             tag: 'div',
15783             cls: 'progress-bar',
15784             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15785         };
15786         
15787         if(this.sr_only){
15788             cfg.cn = {
15789                 tag: 'span',
15790                 cls: 'sr-only',
15791                 html: this.sr_only
15792             }
15793         }
15794         
15795         if(this.role){
15796             cfg.role = this.role;
15797         }
15798         
15799         if(this.aria_valuenow){
15800             cfg['aria-valuenow'] = this.aria_valuenow;
15801         }
15802         
15803         if(this.aria_valuemin){
15804             cfg['aria-valuemin'] = this.aria_valuemin;
15805         }
15806         
15807         if(this.aria_valuemax){
15808             cfg['aria-valuemax'] = this.aria_valuemax;
15809         }
15810         
15811         if(this.label && !this.sr_only){
15812             cfg.html = this.label;
15813         }
15814         
15815         if(this.panel){
15816             cfg.cls += ' progress-bar-' + this.panel;
15817         }
15818         
15819         return cfg;
15820     },
15821     
15822     update : function(aria_valuenow)
15823     {
15824         this.aria_valuenow = aria_valuenow;
15825         
15826         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15827     }
15828    
15829 });
15830
15831  
15832
15833  /*
15834  * - LGPL
15835  *
15836  * column
15837  * 
15838  */
15839
15840 /**
15841  * @class Roo.bootstrap.TabGroup
15842  * @extends Roo.bootstrap.Column
15843  * Bootstrap Column class
15844  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15845  * @cfg {Boolean} carousel true to make the group behave like a carousel
15846  * @cfg {Number} bullets show the panel pointer.. default 0
15847  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15848  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15849  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15850  * 
15851  * @constructor
15852  * Create a new TabGroup
15853  * @param {Object} config The config object
15854  */
15855
15856 Roo.bootstrap.TabGroup = function(config){
15857     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15858     if (!this.navId) {
15859         this.navId = Roo.id();
15860     }
15861     this.tabs = [];
15862     Roo.bootstrap.TabGroup.register(this);
15863     
15864 };
15865
15866 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15867     
15868     carousel : false,
15869     transition : false,
15870     bullets : 0,
15871     timer : 0,
15872     autoslide : false,
15873     slideFn : false,
15874     slideOnTouch : false,
15875     
15876     getAutoCreate : function()
15877     {
15878         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15879         
15880         cfg.cls += ' tab-content';
15881         
15882         Roo.log('get auto create...............');
15883         
15884         if (this.carousel) {
15885             cfg.cls += ' carousel slide';
15886             
15887             cfg.cn = [{
15888                cls : 'carousel-inner'
15889             }];
15890         
15891             if(this.bullets > 0 && !Roo.isTouch){
15892                 
15893                 var bullets = {
15894                     cls : 'carousel-bullets',
15895                     cn : []
15896                 };
15897                 
15898                 if(this.bullets_cls){
15899                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15900                 }
15901                 
15902                 for (var i = 0; i < this.bullets; i++){
15903                     bullets.cn.push({
15904                         cls : 'bullet bullet-' + i
15905                     });
15906                 }
15907                 
15908                 bullets.cn.push({
15909                     cls : 'clear'
15910                 });
15911                 
15912                 cfg.cn[0].cn = bullets;
15913             }
15914         }
15915         
15916         return cfg;
15917     },
15918     
15919     initEvents:  function()
15920     {
15921         Roo.log('-------- init events on tab group ---------');
15922         
15923         if(this.bullets > 0 && !Roo.isTouch){
15924             this.initBullet();
15925         }
15926         
15927         Roo.log(this);
15928         
15929         if(Roo.isTouch && this.slideOnTouch){
15930             this.el.on("touchstart", this.onTouchStart, this);
15931         }
15932         
15933         if(this.autoslide){
15934             var _this = this;
15935             
15936             this.slideFn = window.setInterval(function() {
15937                 _this.showPanelNext();
15938             }, this.timer);
15939         }
15940         
15941     },
15942     
15943     onTouchStart : function(e, el, o)
15944     {
15945         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15946             return;
15947         }
15948         
15949         this.showPanelNext();
15950     },
15951     
15952     getChildContainer : function()
15953     {
15954         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15955     },
15956     
15957     /**
15958     * register a Navigation item
15959     * @param {Roo.bootstrap.NavItem} the navitem to add
15960     */
15961     register : function(item)
15962     {
15963         this.tabs.push( item);
15964         item.navId = this.navId; // not really needed..
15965     
15966     },
15967     
15968     getActivePanel : function()
15969     {
15970         var r = false;
15971         Roo.each(this.tabs, function(t) {
15972             if (t.active) {
15973                 r = t;
15974                 return false;
15975             }
15976             return null;
15977         });
15978         return r;
15979         
15980     },
15981     getPanelByName : function(n)
15982     {
15983         var r = false;
15984         Roo.each(this.tabs, function(t) {
15985             if (t.tabId == n) {
15986                 r = t;
15987                 return false;
15988             }
15989             return null;
15990         });
15991         return r;
15992     },
15993     indexOfPanel : function(p)
15994     {
15995         var r = false;
15996         Roo.each(this.tabs, function(t,i) {
15997             if (t.tabId == p.tabId) {
15998                 r = i;
15999                 return false;
16000             }
16001             return null;
16002         });
16003         return r;
16004     },
16005     /**
16006      * show a specific panel
16007      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16008      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16009      */
16010     showPanel : function (pan)
16011     {
16012         if(this.transition){
16013             Roo.log("waiting for the transitionend");
16014             return;
16015         }
16016         
16017         if (typeof(pan) == 'number') {
16018             pan = this.tabs[pan];
16019         }
16020         if (typeof(pan) == 'string') {
16021             pan = this.getPanelByName(pan);
16022         }
16023         if (pan.tabId == this.getActivePanel().tabId) {
16024             return true;
16025         }
16026         var cur = this.getActivePanel();
16027         
16028         if (false === cur.fireEvent('beforedeactivate')) {
16029             return false;
16030         }
16031         
16032         if(this.bullets > 0 && !Roo.isTouch){
16033             this.setActiveBullet(this.indexOfPanel(pan));
16034         }
16035         
16036         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16037             
16038             this.transition = true;
16039             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16040             var lr = dir == 'next' ? 'left' : 'right';
16041             pan.el.addClass(dir); // or prev
16042             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16043             cur.el.addClass(lr); // or right
16044             pan.el.addClass(lr);
16045             
16046             var _this = this;
16047             cur.el.on('transitionend', function() {
16048                 Roo.log("trans end?");
16049                 
16050                 pan.el.removeClass([lr,dir]);
16051                 pan.setActive(true);
16052                 
16053                 cur.el.removeClass([lr]);
16054                 cur.setActive(false);
16055                 
16056                 _this.transition = false;
16057                 
16058             }, this, { single:  true } );
16059             
16060             return true;
16061         }
16062         
16063         cur.setActive(false);
16064         pan.setActive(true);
16065         
16066         return true;
16067         
16068     },
16069     showPanelNext : function()
16070     {
16071         var i = this.indexOfPanel(this.getActivePanel());
16072         
16073         if (i >= this.tabs.length - 1 && !this.autoslide) {
16074             return;
16075         }
16076         
16077         if (i >= this.tabs.length - 1 && this.autoslide) {
16078             i = -1;
16079         }
16080         
16081         this.showPanel(this.tabs[i+1]);
16082     },
16083     
16084     showPanelPrev : function()
16085     {
16086         var i = this.indexOfPanel(this.getActivePanel());
16087         
16088         if (i  < 1 && !this.autoslide) {
16089             return;
16090         }
16091         
16092         if (i < 1 && this.autoslide) {
16093             i = this.tabs.length;
16094         }
16095         
16096         this.showPanel(this.tabs[i-1]);
16097     },
16098     
16099     initBullet : function()
16100     {
16101         if(Roo.isTouch){
16102             return;
16103         }
16104         
16105         var _this = this;
16106         
16107         for (var i = 0; i < this.bullets; i++){
16108             var bullet = this.el.select('.bullet-' + i, true).first();
16109
16110             if(!bullet){
16111                 continue;
16112             }
16113
16114             bullet.on('click', (function(e, el, o, ii, t){
16115
16116                 e.preventDefault();
16117
16118                 _this.showPanel(ii);
16119
16120                 if(_this.autoslide && _this.slideFn){
16121                     clearInterval(_this.slideFn);
16122                     _this.slideFn = window.setInterval(function() {
16123                         _this.showPanelNext();
16124                     }, _this.timer);
16125                 }
16126
16127             }).createDelegate(this, [i, bullet], true));
16128         }
16129     },
16130     
16131     setActiveBullet : function(i)
16132     {
16133         if(Roo.isTouch){
16134             return;
16135         }
16136         
16137         Roo.each(this.el.select('.bullet', true).elements, function(el){
16138             el.removeClass('selected');
16139         });
16140
16141         var bullet = this.el.select('.bullet-' + i, true).first();
16142         
16143         if(!bullet){
16144             return;
16145         }
16146         
16147         bullet.addClass('selected');
16148     }
16149     
16150     
16151   
16152 });
16153
16154  
16155
16156  
16157  
16158 Roo.apply(Roo.bootstrap.TabGroup, {
16159     
16160     groups: {},
16161      /**
16162     * register a Navigation Group
16163     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16164     */
16165     register : function(navgrp)
16166     {
16167         this.groups[navgrp.navId] = navgrp;
16168         
16169     },
16170     /**
16171     * fetch a Navigation Group based on the navigation ID
16172     * if one does not exist , it will get created.
16173     * @param {string} the navgroup to add
16174     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16175     */
16176     get: function(navId) {
16177         if (typeof(this.groups[navId]) == 'undefined') {
16178             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16179         }
16180         return this.groups[navId] ;
16181     }
16182     
16183     
16184     
16185 });
16186
16187  /*
16188  * - LGPL
16189  *
16190  * TabPanel
16191  * 
16192  */
16193
16194 /**
16195  * @class Roo.bootstrap.TabPanel
16196  * @extends Roo.bootstrap.Component
16197  * Bootstrap TabPanel class
16198  * @cfg {Boolean} active panel active
16199  * @cfg {String} html panel content
16200  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16201  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16202  * 
16203  * 
16204  * @constructor
16205  * Create a new TabPanel
16206  * @param {Object} config The config object
16207  */
16208
16209 Roo.bootstrap.TabPanel = function(config){
16210     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16211     this.addEvents({
16212         /**
16213              * @event changed
16214              * Fires when the active status changes
16215              * @param {Roo.bootstrap.TabPanel} this
16216              * @param {Boolean} state the new state
16217             
16218          */
16219         'changed': true,
16220         /**
16221              * @event beforedeactivate
16222              * Fires before a tab is de-activated - can be used to do validation on a form.
16223              * @param {Roo.bootstrap.TabPanel} this
16224              * @return {Boolean} false if there is an error
16225             
16226          */
16227         'beforedeactivate': true
16228      });
16229     
16230     this.tabId = this.tabId || Roo.id();
16231   
16232 };
16233
16234 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16235     
16236     active: false,
16237     html: false,
16238     tabId: false,
16239     navId : false,
16240     
16241     getAutoCreate : function(){
16242         var cfg = {
16243             tag: 'div',
16244             // item is needed for carousel - not sure if it has any effect otherwise
16245             cls: 'tab-pane item',
16246             html: this.html || ''
16247         };
16248         
16249         if(this.active){
16250             cfg.cls += ' active';
16251         }
16252         
16253         if(this.tabId){
16254             cfg.tabId = this.tabId;
16255         }
16256         
16257         
16258         return cfg;
16259     },
16260     
16261     initEvents:  function()
16262     {
16263         Roo.log('-------- init events on tab panel ---------');
16264         
16265         var p = this.parent();
16266         this.navId = this.navId || p.navId;
16267         
16268         if (typeof(this.navId) != 'undefined') {
16269             // not really needed.. but just in case.. parent should be a NavGroup.
16270             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16271             Roo.log(['register', tg, this]);
16272             tg.register(this);
16273             
16274             var i = tg.tabs.length - 1;
16275             
16276             if(this.active && tg.bullets > 0 && i < tg.bullets){
16277                 tg.setActiveBullet(i);
16278             }
16279         }
16280         
16281     },
16282     
16283     
16284     onRender : function(ct, position)
16285     {
16286        // Roo.log("Call onRender: " + this.xtype);
16287         
16288         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16289         
16290         
16291         
16292         
16293         
16294     },
16295     
16296     setActive: function(state)
16297     {
16298         Roo.log("panel - set active " + this.tabId + "=" + state);
16299         
16300         this.active = state;
16301         if (!state) {
16302             this.el.removeClass('active');
16303             
16304         } else  if (!this.el.hasClass('active')) {
16305             this.el.addClass('active');
16306         }
16307         
16308         this.fireEvent('changed', this, state);
16309     }
16310     
16311     
16312 });
16313  
16314
16315  
16316
16317  /*
16318  * - LGPL
16319  *
16320  * DateField
16321  * 
16322  */
16323
16324 /**
16325  * @class Roo.bootstrap.DateField
16326  * @extends Roo.bootstrap.Input
16327  * Bootstrap DateField class
16328  * @cfg {Number} weekStart default 0
16329  * @cfg {String} viewMode default empty, (months|years)
16330  * @cfg {String} minViewMode default empty, (months|years)
16331  * @cfg {Number} startDate default -Infinity
16332  * @cfg {Number} endDate default Infinity
16333  * @cfg {Boolean} todayHighlight default false
16334  * @cfg {Boolean} todayBtn default false
16335  * @cfg {Boolean} calendarWeeks default false
16336  * @cfg {Object} daysOfWeekDisabled default empty
16337  * @cfg {Boolean} singleMode default false (true | false)
16338  * 
16339  * @cfg {Boolean} keyboardNavigation default true
16340  * @cfg {String} language default en
16341  * 
16342  * @constructor
16343  * Create a new DateField
16344  * @param {Object} config The config object
16345  */
16346
16347 Roo.bootstrap.DateField = function(config){
16348     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16349      this.addEvents({
16350             /**
16351              * @event show
16352              * Fires when this field show.
16353              * @param {Roo.bootstrap.DateField} this
16354              * @param {Mixed} date The date value
16355              */
16356             show : true,
16357             /**
16358              * @event show
16359              * Fires when this field hide.
16360              * @param {Roo.bootstrap.DateField} this
16361              * @param {Mixed} date The date value
16362              */
16363             hide : true,
16364             /**
16365              * @event select
16366              * Fires when select a date.
16367              * @param {Roo.bootstrap.DateField} this
16368              * @param {Mixed} date The date value
16369              */
16370             select : true,
16371             /**
16372              * @event beforeselect
16373              * Fires when before select a date.
16374              * @param {Roo.bootstrap.DateField} this
16375              * @param {Mixed} date The date value
16376              */
16377             beforeselect : true
16378         });
16379 };
16380
16381 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16382     
16383     /**
16384      * @cfg {String} format
16385      * The default date format string which can be overriden for localization support.  The format must be
16386      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16387      */
16388     format : "m/d/y",
16389     /**
16390      * @cfg {String} altFormats
16391      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16392      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16393      */
16394     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16395     
16396     weekStart : 0,
16397     
16398     viewMode : '',
16399     
16400     minViewMode : '',
16401     
16402     todayHighlight : false,
16403     
16404     todayBtn: false,
16405     
16406     language: 'en',
16407     
16408     keyboardNavigation: true,
16409     
16410     calendarWeeks: false,
16411     
16412     startDate: -Infinity,
16413     
16414     endDate: Infinity,
16415     
16416     daysOfWeekDisabled: [],
16417     
16418     _events: [],
16419     
16420     singleMode : false,
16421     
16422     UTCDate: function()
16423     {
16424         return new Date(Date.UTC.apply(Date, arguments));
16425     },
16426     
16427     UTCToday: function()
16428     {
16429         var today = new Date();
16430         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16431     },
16432     
16433     getDate: function() {
16434             var d = this.getUTCDate();
16435             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16436     },
16437     
16438     getUTCDate: function() {
16439             return this.date;
16440     },
16441     
16442     setDate: function(d) {
16443             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16444     },
16445     
16446     setUTCDate: function(d) {
16447             this.date = d;
16448             this.setValue(this.formatDate(this.date));
16449     },
16450         
16451     onRender: function(ct, position)
16452     {
16453         
16454         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16455         
16456         this.language = this.language || 'en';
16457         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16458         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16459         
16460         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16461         this.format = this.format || 'm/d/y';
16462         this.isInline = false;
16463         this.isInput = true;
16464         this.component = this.el.select('.add-on', true).first() || false;
16465         this.component = (this.component && this.component.length === 0) ? false : this.component;
16466         this.hasInput = this.component && this.inputEL().length;
16467         
16468         if (typeof(this.minViewMode === 'string')) {
16469             switch (this.minViewMode) {
16470                 case 'months':
16471                     this.minViewMode = 1;
16472                     break;
16473                 case 'years':
16474                     this.minViewMode = 2;
16475                     break;
16476                 default:
16477                     this.minViewMode = 0;
16478                     break;
16479             }
16480         }
16481         
16482         if (typeof(this.viewMode === 'string')) {
16483             switch (this.viewMode) {
16484                 case 'months':
16485                     this.viewMode = 1;
16486                     break;
16487                 case 'years':
16488                     this.viewMode = 2;
16489                     break;
16490                 default:
16491                     this.viewMode = 0;
16492                     break;
16493             }
16494         }
16495                 
16496         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16497         
16498 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16499         
16500         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16501         
16502         this.picker().on('mousedown', this.onMousedown, this);
16503         this.picker().on('click', this.onClick, this);
16504         
16505         this.picker().addClass('datepicker-dropdown');
16506         
16507         this.startViewMode = this.viewMode;
16508         
16509         if(this.singleMode){
16510             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16511                 v.setVisibilityMode(Roo.Element.DISPLAY)
16512                 v.hide();
16513             });
16514             
16515             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16516                 v.setStyle('width', '189px');
16517             });
16518         }
16519         
16520         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16521             if(!this.calendarWeeks){
16522                 v.remove();
16523                 return;
16524             }
16525             
16526             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16527             v.attr('colspan', function(i, val){
16528                 return parseInt(val) + 1;
16529             });
16530         })
16531                         
16532         
16533         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16534         
16535         this.setStartDate(this.startDate);
16536         this.setEndDate(this.endDate);
16537         
16538         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16539         
16540         this.fillDow();
16541         this.fillMonths();
16542         this.update();
16543         this.showMode();
16544         
16545         if(this.isInline) {
16546             this.show();
16547         }
16548     },
16549     
16550     picker : function()
16551     {
16552         return this.pickerEl;
16553 //        return this.el.select('.datepicker', true).first();
16554     },
16555     
16556     fillDow: function()
16557     {
16558         var dowCnt = this.weekStart;
16559         
16560         var dow = {
16561             tag: 'tr',
16562             cn: [
16563                 
16564             ]
16565         };
16566         
16567         if(this.calendarWeeks){
16568             dow.cn.push({
16569                 tag: 'th',
16570                 cls: 'cw',
16571                 html: '&nbsp;'
16572             })
16573         }
16574         
16575         while (dowCnt < this.weekStart + 7) {
16576             dow.cn.push({
16577                 tag: 'th',
16578                 cls: 'dow',
16579                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16580             });
16581         }
16582         
16583         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16584     },
16585     
16586     fillMonths: function()
16587     {    
16588         var i = 0;
16589         var months = this.picker().select('>.datepicker-months td', true).first();
16590         
16591         months.dom.innerHTML = '';
16592         
16593         while (i < 12) {
16594             var month = {
16595                 tag: 'span',
16596                 cls: 'month',
16597                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16598             };
16599             
16600             months.createChild(month);
16601         }
16602         
16603     },
16604     
16605     update: function()
16606     {
16607         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
16608         
16609         if (this.date < this.startDate) {
16610             this.viewDate = new Date(this.startDate);
16611         } else if (this.date > this.endDate) {
16612             this.viewDate = new Date(this.endDate);
16613         } else {
16614             this.viewDate = new Date(this.date);
16615         }
16616         
16617         this.fill();
16618     },
16619     
16620     fill: function() 
16621     {
16622         var d = new Date(this.viewDate),
16623                 year = d.getUTCFullYear(),
16624                 month = d.getUTCMonth(),
16625                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16626                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16627                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16628                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16629                 currentDate = this.date && this.date.valueOf(),
16630                 today = this.UTCToday();
16631         
16632         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16633         
16634 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16635         
16636 //        this.picker.select('>tfoot th.today').
16637 //                                              .text(dates[this.language].today)
16638 //                                              .toggle(this.todayBtn !== false);
16639     
16640         this.updateNavArrows();
16641         this.fillMonths();
16642                                                 
16643         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16644         
16645         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16646          
16647         prevMonth.setUTCDate(day);
16648         
16649         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16650         
16651         var nextMonth = new Date(prevMonth);
16652         
16653         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16654         
16655         nextMonth = nextMonth.valueOf();
16656         
16657         var fillMonths = false;
16658         
16659         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16660         
16661         while(prevMonth.valueOf() < nextMonth) {
16662             var clsName = '';
16663             
16664             if (prevMonth.getUTCDay() === this.weekStart) {
16665                 if(fillMonths){
16666                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16667                 }
16668                     
16669                 fillMonths = {
16670                     tag: 'tr',
16671                     cn: []
16672                 };
16673                 
16674                 if(this.calendarWeeks){
16675                     // ISO 8601: First week contains first thursday.
16676                     // ISO also states week starts on Monday, but we can be more abstract here.
16677                     var
16678                     // Start of current week: based on weekstart/current date
16679                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16680                     // Thursday of this week
16681                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16682                     // First Thursday of year, year from thursday
16683                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16684                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16685                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16686                     
16687                     fillMonths.cn.push({
16688                         tag: 'td',
16689                         cls: 'cw',
16690                         html: calWeek
16691                     });
16692                 }
16693             }
16694             
16695             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16696                 clsName += ' old';
16697             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16698                 clsName += ' new';
16699             }
16700             if (this.todayHighlight &&
16701                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16702                 prevMonth.getUTCMonth() == today.getMonth() &&
16703                 prevMonth.getUTCDate() == today.getDate()) {
16704                 clsName += ' today';
16705             }
16706             
16707             if (currentDate && prevMonth.valueOf() === currentDate) {
16708                 clsName += ' active';
16709             }
16710             
16711             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16712                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16713                     clsName += ' disabled';
16714             }
16715             
16716             fillMonths.cn.push({
16717                 tag: 'td',
16718                 cls: 'day ' + clsName,
16719                 html: prevMonth.getDate()
16720             })
16721             
16722             prevMonth.setDate(prevMonth.getDate()+1);
16723         }
16724           
16725         var currentYear = this.date && this.date.getUTCFullYear();
16726         var currentMonth = this.date && this.date.getUTCMonth();
16727         
16728         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16729         
16730         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16731             v.removeClass('active');
16732             
16733             if(currentYear === year && k === currentMonth){
16734                 v.addClass('active');
16735             }
16736             
16737             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16738                 v.addClass('disabled');
16739             }
16740             
16741         });
16742         
16743         
16744         year = parseInt(year/10, 10) * 10;
16745         
16746         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16747         
16748         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16749         
16750         year -= 1;
16751         for (var i = -1; i < 11; i++) {
16752             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16753                 tag: 'span',
16754                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16755                 html: year
16756             })
16757             
16758             year += 1;
16759         }
16760     },
16761     
16762     showMode: function(dir) 
16763     {
16764         if (dir) {
16765             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16766         }
16767         
16768         Roo.each(this.picker().select('>div',true).elements, function(v){
16769             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16770             v.hide();
16771         });
16772         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16773     },
16774     
16775     place: function()
16776     {
16777         if(this.isInline) return;
16778         
16779         this.picker().removeClass(['bottom', 'top']);
16780         
16781         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16782             /*
16783              * place to the top of element!
16784              *
16785              */
16786             
16787             this.picker().addClass('top');
16788             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16789             
16790             return;
16791         }
16792         
16793         this.picker().addClass('bottom');
16794         
16795         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16796     },
16797     
16798     parseDate : function(value)
16799     {
16800         if(!value || value instanceof Date){
16801             return value;
16802         }
16803         var v = Date.parseDate(value, this.format);
16804         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16805             v = Date.parseDate(value, 'Y-m-d');
16806         }
16807         if(!v && this.altFormats){
16808             if(!this.altFormatsArray){
16809                 this.altFormatsArray = this.altFormats.split("|");
16810             }
16811             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16812                 v = Date.parseDate(value, this.altFormatsArray[i]);
16813             }
16814         }
16815         return v;
16816     },
16817     
16818     formatDate : function(date, fmt)
16819     {   
16820         return (!date || !(date instanceof Date)) ?
16821         date : date.dateFormat(fmt || this.format);
16822     },
16823     
16824     onFocus : function()
16825     {
16826         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16827         this.show();
16828     },
16829     
16830     onBlur : function()
16831     {
16832         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16833         
16834         var d = this.inputEl().getValue();
16835         
16836         this.setValue(d);
16837                 
16838         this.hide();
16839     },
16840     
16841     show : function()
16842     {
16843         this.picker().show();
16844         this.update();
16845         this.place();
16846         
16847         this.fireEvent('show', this, this.date);
16848     },
16849     
16850     hide : function()
16851     {
16852         if(this.isInline) return;
16853         this.picker().hide();
16854         this.viewMode = this.startViewMode;
16855         this.showMode();
16856         
16857         this.fireEvent('hide', this, this.date);
16858         
16859     },
16860     
16861     onMousedown: function(e)
16862     {
16863         e.stopPropagation();
16864         e.preventDefault();
16865     },
16866     
16867     keyup: function(e)
16868     {
16869         Roo.bootstrap.DateField.superclass.keyup.call(this);
16870         this.update();
16871     },
16872
16873     setValue: function(v)
16874     {
16875         if(this.fireEvent('beforeselect', this, v) !== false){
16876             var d = new Date(this.parseDate(v) ).clearTime();
16877         
16878             if(isNaN(d.getTime())){
16879                 this.date = this.viewDate = '';
16880                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16881                 return;
16882             }
16883
16884             v = this.formatDate(d);
16885
16886             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16887
16888             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16889
16890             this.update();
16891
16892             this.fireEvent('select', this, this.date);
16893         }
16894     },
16895     
16896     getValue: function()
16897     {
16898         return this.formatDate(this.date);
16899     },
16900     
16901     fireKey: function(e)
16902     {
16903         if (!this.picker().isVisible()){
16904             if (e.keyCode == 27) // allow escape to hide and re-show picker
16905                 this.show();
16906             return;
16907         }
16908         
16909         var dateChanged = false,
16910         dir, day, month,
16911         newDate, newViewDate;
16912         
16913         switch(e.keyCode){
16914             case 27: // escape
16915                 this.hide();
16916                 e.preventDefault();
16917                 break;
16918             case 37: // left
16919             case 39: // right
16920                 if (!this.keyboardNavigation) break;
16921                 dir = e.keyCode == 37 ? -1 : 1;
16922                 
16923                 if (e.ctrlKey){
16924                     newDate = this.moveYear(this.date, dir);
16925                     newViewDate = this.moveYear(this.viewDate, dir);
16926                 } else if (e.shiftKey){
16927                     newDate = this.moveMonth(this.date, dir);
16928                     newViewDate = this.moveMonth(this.viewDate, dir);
16929                 } else {
16930                     newDate = new Date(this.date);
16931                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16932                     newViewDate = new Date(this.viewDate);
16933                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16934                 }
16935                 if (this.dateWithinRange(newDate)){
16936                     this.date = newDate;
16937                     this.viewDate = newViewDate;
16938                     this.setValue(this.formatDate(this.date));
16939 //                    this.update();
16940                     e.preventDefault();
16941                     dateChanged = true;
16942                 }
16943                 break;
16944             case 38: // up
16945             case 40: // down
16946                 if (!this.keyboardNavigation) break;
16947                 dir = e.keyCode == 38 ? -1 : 1;
16948                 if (e.ctrlKey){
16949                     newDate = this.moveYear(this.date, dir);
16950                     newViewDate = this.moveYear(this.viewDate, dir);
16951                 } else if (e.shiftKey){
16952                     newDate = this.moveMonth(this.date, dir);
16953                     newViewDate = this.moveMonth(this.viewDate, dir);
16954                 } else {
16955                     newDate = new Date(this.date);
16956                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16957                     newViewDate = new Date(this.viewDate);
16958                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16959                 }
16960                 if (this.dateWithinRange(newDate)){
16961                     this.date = newDate;
16962                     this.viewDate = newViewDate;
16963                     this.setValue(this.formatDate(this.date));
16964 //                    this.update();
16965                     e.preventDefault();
16966                     dateChanged = true;
16967                 }
16968                 break;
16969             case 13: // enter
16970                 this.setValue(this.formatDate(this.date));
16971                 this.hide();
16972                 e.preventDefault();
16973                 break;
16974             case 9: // tab
16975                 this.setValue(this.formatDate(this.date));
16976                 this.hide();
16977                 break;
16978             case 16: // shift
16979             case 17: // ctrl
16980             case 18: // alt
16981                 break;
16982             default :
16983                 this.hide();
16984                 
16985         }
16986     },
16987     
16988     
16989     onClick: function(e) 
16990     {
16991         e.stopPropagation();
16992         e.preventDefault();
16993         
16994         var target = e.getTarget();
16995         
16996         if(target.nodeName.toLowerCase() === 'i'){
16997             target = Roo.get(target).dom.parentNode;
16998         }
16999         
17000         var nodeName = target.nodeName;
17001         var className = target.className;
17002         var html = target.innerHTML;
17003         //Roo.log(nodeName);
17004         
17005         switch(nodeName.toLowerCase()) {
17006             case 'th':
17007                 switch(className) {
17008                     case 'switch':
17009                         this.showMode(1);
17010                         break;
17011                     case 'prev':
17012                     case 'next':
17013                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17014                         switch(this.viewMode){
17015                                 case 0:
17016                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17017                                         break;
17018                                 case 1:
17019                                 case 2:
17020                                         this.viewDate = this.moveYear(this.viewDate, dir);
17021                                         break;
17022                         }
17023                         this.fill();
17024                         break;
17025                     case 'today':
17026                         var date = new Date();
17027                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17028 //                        this.fill()
17029                         this.setValue(this.formatDate(this.date));
17030                         
17031                         this.hide();
17032                         break;
17033                 }
17034                 break;
17035             case 'span':
17036                 if (className.indexOf('disabled') < 0) {
17037                     this.viewDate.setUTCDate(1);
17038                     if (className.indexOf('month') > -1) {
17039                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17040                     } else {
17041                         var year = parseInt(html, 10) || 0;
17042                         this.viewDate.setUTCFullYear(year);
17043                         
17044                     }
17045                     
17046                     if(this.singleMode){
17047                         this.setValue(this.formatDate(this.viewDate));
17048                         this.hide();
17049                         return;
17050                     }
17051                     
17052                     this.showMode(-1);
17053                     this.fill();
17054                 }
17055                 break;
17056                 
17057             case 'td':
17058                 //Roo.log(className);
17059                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17060                     var day = parseInt(html, 10) || 1;
17061                     var year = this.viewDate.getUTCFullYear(),
17062                         month = this.viewDate.getUTCMonth();
17063
17064                     if (className.indexOf('old') > -1) {
17065                         if(month === 0 ){
17066                             month = 11;
17067                             year -= 1;
17068                         }else{
17069                             month -= 1;
17070                         }
17071                     } else if (className.indexOf('new') > -1) {
17072                         if (month == 11) {
17073                             month = 0;
17074                             year += 1;
17075                         } else {
17076                             month += 1;
17077                         }
17078                     }
17079                     //Roo.log([year,month,day]);
17080                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17081                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17082 //                    this.fill();
17083                     //Roo.log(this.formatDate(this.date));
17084                     this.setValue(this.formatDate(this.date));
17085                     this.hide();
17086                 }
17087                 break;
17088         }
17089     },
17090     
17091     setStartDate: function(startDate)
17092     {
17093         this.startDate = startDate || -Infinity;
17094         if (this.startDate !== -Infinity) {
17095             this.startDate = this.parseDate(this.startDate);
17096         }
17097         this.update();
17098         this.updateNavArrows();
17099     },
17100
17101     setEndDate: function(endDate)
17102     {
17103         this.endDate = endDate || Infinity;
17104         if (this.endDate !== Infinity) {
17105             this.endDate = this.parseDate(this.endDate);
17106         }
17107         this.update();
17108         this.updateNavArrows();
17109     },
17110     
17111     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17112     {
17113         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17114         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17115             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17116         }
17117         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17118             return parseInt(d, 10);
17119         });
17120         this.update();
17121         this.updateNavArrows();
17122     },
17123     
17124     updateNavArrows: function() 
17125     {
17126         if(this.singleMode){
17127             return;
17128         }
17129         
17130         var d = new Date(this.viewDate),
17131         year = d.getUTCFullYear(),
17132         month = d.getUTCMonth();
17133         
17134         Roo.each(this.picker().select('.prev', true).elements, function(v){
17135             v.show();
17136             switch (this.viewMode) {
17137                 case 0:
17138
17139                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17140                         v.hide();
17141                     }
17142                     break;
17143                 case 1:
17144                 case 2:
17145                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17146                         v.hide();
17147                     }
17148                     break;
17149             }
17150         });
17151         
17152         Roo.each(this.picker().select('.next', true).elements, function(v){
17153             v.show();
17154             switch (this.viewMode) {
17155                 case 0:
17156
17157                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17158                         v.hide();
17159                     }
17160                     break;
17161                 case 1:
17162                 case 2:
17163                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17164                         v.hide();
17165                     }
17166                     break;
17167             }
17168         })
17169     },
17170     
17171     moveMonth: function(date, dir)
17172     {
17173         if (!dir) return date;
17174         var new_date = new Date(date.valueOf()),
17175         day = new_date.getUTCDate(),
17176         month = new_date.getUTCMonth(),
17177         mag = Math.abs(dir),
17178         new_month, test;
17179         dir = dir > 0 ? 1 : -1;
17180         if (mag == 1){
17181             test = dir == -1
17182             // If going back one month, make sure month is not current month
17183             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17184             ? function(){
17185                 return new_date.getUTCMonth() == month;
17186             }
17187             // If going forward one month, make sure month is as expected
17188             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17189             : function(){
17190                 return new_date.getUTCMonth() != new_month;
17191             };
17192             new_month = month + dir;
17193             new_date.setUTCMonth(new_month);
17194             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17195             if (new_month < 0 || new_month > 11)
17196                 new_month = (new_month + 12) % 12;
17197         } else {
17198             // For magnitudes >1, move one month at a time...
17199             for (var i=0; i<mag; i++)
17200                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17201                 new_date = this.moveMonth(new_date, dir);
17202             // ...then reset the day, keeping it in the new month
17203             new_month = new_date.getUTCMonth();
17204             new_date.setUTCDate(day);
17205             test = function(){
17206                 return new_month != new_date.getUTCMonth();
17207             };
17208         }
17209         // Common date-resetting loop -- if date is beyond end of month, make it
17210         // end of month
17211         while (test()){
17212             new_date.setUTCDate(--day);
17213             new_date.setUTCMonth(new_month);
17214         }
17215         return new_date;
17216     },
17217
17218     moveYear: function(date, dir)
17219     {
17220         return this.moveMonth(date, dir*12);
17221     },
17222
17223     dateWithinRange: function(date)
17224     {
17225         return date >= this.startDate && date <= this.endDate;
17226     },
17227
17228     
17229     remove: function() 
17230     {
17231         this.picker().remove();
17232     }
17233    
17234 });
17235
17236 Roo.apply(Roo.bootstrap.DateField,  {
17237     
17238     head : {
17239         tag: 'thead',
17240         cn: [
17241         {
17242             tag: 'tr',
17243             cn: [
17244             {
17245                 tag: 'th',
17246                 cls: 'prev',
17247                 html: '<i class="fa fa-arrow-left"/>'
17248             },
17249             {
17250                 tag: 'th',
17251                 cls: 'switch',
17252                 colspan: '5'
17253             },
17254             {
17255                 tag: 'th',
17256                 cls: 'next',
17257                 html: '<i class="fa fa-arrow-right"/>'
17258             }
17259
17260             ]
17261         }
17262         ]
17263     },
17264     
17265     content : {
17266         tag: 'tbody',
17267         cn: [
17268         {
17269             tag: 'tr',
17270             cn: [
17271             {
17272                 tag: 'td',
17273                 colspan: '7'
17274             }
17275             ]
17276         }
17277         ]
17278     },
17279     
17280     footer : {
17281         tag: 'tfoot',
17282         cn: [
17283         {
17284             tag: 'tr',
17285             cn: [
17286             {
17287                 tag: 'th',
17288                 colspan: '7',
17289                 cls: 'today'
17290             }
17291                     
17292             ]
17293         }
17294         ]
17295     },
17296     
17297     dates:{
17298         en: {
17299             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17300             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17301             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17302             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17303             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17304             today: "Today"
17305         }
17306     },
17307     
17308     modes: [
17309     {
17310         clsName: 'days',
17311         navFnc: 'Month',
17312         navStep: 1
17313     },
17314     {
17315         clsName: 'months',
17316         navFnc: 'FullYear',
17317         navStep: 1
17318     },
17319     {
17320         clsName: 'years',
17321         navFnc: 'FullYear',
17322         navStep: 10
17323     }]
17324 });
17325
17326 Roo.apply(Roo.bootstrap.DateField,  {
17327   
17328     template : {
17329         tag: 'div',
17330         cls: 'datepicker dropdown-menu roo-dynamic',
17331         cn: [
17332         {
17333             tag: 'div',
17334             cls: 'datepicker-days',
17335             cn: [
17336             {
17337                 tag: 'table',
17338                 cls: 'table-condensed',
17339                 cn:[
17340                 Roo.bootstrap.DateField.head,
17341                 {
17342                     tag: 'tbody'
17343                 },
17344                 Roo.bootstrap.DateField.footer
17345                 ]
17346             }
17347             ]
17348         },
17349         {
17350             tag: 'div',
17351             cls: 'datepicker-months',
17352             cn: [
17353             {
17354                 tag: 'table',
17355                 cls: 'table-condensed',
17356                 cn:[
17357                 Roo.bootstrap.DateField.head,
17358                 Roo.bootstrap.DateField.content,
17359                 Roo.bootstrap.DateField.footer
17360                 ]
17361             }
17362             ]
17363         },
17364         {
17365             tag: 'div',
17366             cls: 'datepicker-years',
17367             cn: [
17368             {
17369                 tag: 'table',
17370                 cls: 'table-condensed',
17371                 cn:[
17372                 Roo.bootstrap.DateField.head,
17373                 Roo.bootstrap.DateField.content,
17374                 Roo.bootstrap.DateField.footer
17375                 ]
17376             }
17377             ]
17378         }
17379         ]
17380     }
17381 });
17382
17383  
17384
17385  /*
17386  * - LGPL
17387  *
17388  * TimeField
17389  * 
17390  */
17391
17392 /**
17393  * @class Roo.bootstrap.TimeField
17394  * @extends Roo.bootstrap.Input
17395  * Bootstrap DateField class
17396  * 
17397  * 
17398  * @constructor
17399  * Create a new TimeField
17400  * @param {Object} config The config object
17401  */
17402
17403 Roo.bootstrap.TimeField = function(config){
17404     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17405     this.addEvents({
17406             /**
17407              * @event show
17408              * Fires when this field show.
17409              * @param {Roo.bootstrap.DateField} thisthis
17410              * @param {Mixed} date The date value
17411              */
17412             show : true,
17413             /**
17414              * @event show
17415              * Fires when this field hide.
17416              * @param {Roo.bootstrap.DateField} this
17417              * @param {Mixed} date The date value
17418              */
17419             hide : true,
17420             /**
17421              * @event select
17422              * Fires when select a date.
17423              * @param {Roo.bootstrap.DateField} this
17424              * @param {Mixed} date The date value
17425              */
17426             select : true
17427         });
17428 };
17429
17430 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17431     
17432     /**
17433      * @cfg {String} format
17434      * The default time format string which can be overriden for localization support.  The format must be
17435      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17436      */
17437     format : "H:i",
17438        
17439     onRender: function(ct, position)
17440     {
17441         
17442         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17443                 
17444         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17445         
17446         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17447         
17448         this.pop = this.picker().select('>.datepicker-time',true).first();
17449         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17450         
17451         this.picker().on('mousedown', this.onMousedown, this);
17452         this.picker().on('click', this.onClick, this);
17453         
17454         this.picker().addClass('datepicker-dropdown');
17455     
17456         this.fillTime();
17457         this.update();
17458             
17459         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17460         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17461         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17462         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17463         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17464         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17465
17466     },
17467     
17468     fireKey: function(e){
17469         if (!this.picker().isVisible()){
17470             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17471                 this.show();
17472             }
17473             return;
17474         }
17475
17476         e.preventDefault();
17477         
17478         switch(e.keyCode){
17479             case 27: // escape
17480                 this.hide();
17481                 break;
17482             case 37: // left
17483             case 39: // right
17484                 this.onTogglePeriod();
17485                 break;
17486             case 38: // up
17487                 this.onIncrementMinutes();
17488                 break;
17489             case 40: // down
17490                 this.onDecrementMinutes();
17491                 break;
17492             case 13: // enter
17493             case 9: // tab
17494                 this.setTime();
17495                 break;
17496         }
17497     },
17498     
17499     onClick: function(e) {
17500         e.stopPropagation();
17501         e.preventDefault();
17502     },
17503     
17504     picker : function()
17505     {
17506         return this.el.select('.datepicker', true).first();
17507     },
17508     
17509     fillTime: function()
17510     {    
17511         var time = this.pop.select('tbody', true).first();
17512         
17513         time.dom.innerHTML = '';
17514         
17515         time.createChild({
17516             tag: 'tr',
17517             cn: [
17518                 {
17519                     tag: 'td',
17520                     cn: [
17521                         {
17522                             tag: 'a',
17523                             href: '#',
17524                             cls: 'btn',
17525                             cn: [
17526                                 {
17527                                     tag: 'span',
17528                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17529                                 }
17530                             ]
17531                         } 
17532                     ]
17533                 },
17534                 {
17535                     tag: 'td',
17536                     cls: 'separator'
17537                 },
17538                 {
17539                     tag: 'td',
17540                     cn: [
17541                         {
17542                             tag: 'a',
17543                             href: '#',
17544                             cls: 'btn',
17545                             cn: [
17546                                 {
17547                                     tag: 'span',
17548                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17549                                 }
17550                             ]
17551                         }
17552                     ]
17553                 },
17554                 {
17555                     tag: 'td',
17556                     cls: 'separator'
17557                 }
17558             ]
17559         });
17560         
17561         time.createChild({
17562             tag: 'tr',
17563             cn: [
17564                 {
17565                     tag: 'td',
17566                     cn: [
17567                         {
17568                             tag: 'span',
17569                             cls: 'timepicker-hour',
17570                             html: '00'
17571                         }  
17572                     ]
17573                 },
17574                 {
17575                     tag: 'td',
17576                     cls: 'separator',
17577                     html: ':'
17578                 },
17579                 {
17580                     tag: 'td',
17581                     cn: [
17582                         {
17583                             tag: 'span',
17584                             cls: 'timepicker-minute',
17585                             html: '00'
17586                         }  
17587                     ]
17588                 },
17589                 {
17590                     tag: 'td',
17591                     cls: 'separator'
17592                 },
17593                 {
17594                     tag: 'td',
17595                     cn: [
17596                         {
17597                             tag: 'button',
17598                             type: 'button',
17599                             cls: 'btn btn-primary period',
17600                             html: 'AM'
17601                             
17602                         }
17603                     ]
17604                 }
17605             ]
17606         });
17607         
17608         time.createChild({
17609             tag: 'tr',
17610             cn: [
17611                 {
17612                     tag: 'td',
17613                     cn: [
17614                         {
17615                             tag: 'a',
17616                             href: '#',
17617                             cls: 'btn',
17618                             cn: [
17619                                 {
17620                                     tag: 'span',
17621                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17622                                 }
17623                             ]
17624                         }
17625                     ]
17626                 },
17627                 {
17628                     tag: 'td',
17629                     cls: 'separator'
17630                 },
17631                 {
17632                     tag: 'td',
17633                     cn: [
17634                         {
17635                             tag: 'a',
17636                             href: '#',
17637                             cls: 'btn',
17638                             cn: [
17639                                 {
17640                                     tag: 'span',
17641                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17642                                 }
17643                             ]
17644                         }
17645                     ]
17646                 },
17647                 {
17648                     tag: 'td',
17649                     cls: 'separator'
17650                 }
17651             ]
17652         });
17653         
17654     },
17655     
17656     update: function()
17657     {
17658         
17659         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17660         
17661         this.fill();
17662     },
17663     
17664     fill: function() 
17665     {
17666         var hours = this.time.getHours();
17667         var minutes = this.time.getMinutes();
17668         var period = 'AM';
17669         
17670         if(hours > 11){
17671             period = 'PM';
17672         }
17673         
17674         if(hours == 0){
17675             hours = 12;
17676         }
17677         
17678         
17679         if(hours > 12){
17680             hours = hours - 12;
17681         }
17682         
17683         if(hours < 10){
17684             hours = '0' + hours;
17685         }
17686         
17687         if(minutes < 10){
17688             minutes = '0' + minutes;
17689         }
17690         
17691         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17692         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17693         this.pop.select('button', true).first().dom.innerHTML = period;
17694         
17695     },
17696     
17697     place: function()
17698     {   
17699         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17700         
17701         var cls = ['bottom'];
17702         
17703         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17704             cls.pop();
17705             cls.push('top');
17706         }
17707         
17708         cls.push('right');
17709         
17710         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17711             cls.pop();
17712             cls.push('left');
17713         }
17714         
17715         this.picker().addClass(cls.join('-'));
17716         
17717         var _this = this;
17718         
17719         Roo.each(cls, function(c){
17720             if(c == 'bottom'){
17721                 _this.picker().setTop(_this.inputEl().getHeight());
17722                 return;
17723             }
17724             if(c == 'top'){
17725                 _this.picker().setTop(0 - _this.picker().getHeight());
17726                 return;
17727             }
17728             
17729             if(c == 'left'){
17730                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17731                 return;
17732             }
17733             if(c == 'right'){
17734                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17735                 return;
17736             }
17737         });
17738         
17739     },
17740   
17741     onFocus : function()
17742     {
17743         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17744         this.show();
17745     },
17746     
17747     onBlur : function()
17748     {
17749         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17750         this.hide();
17751     },
17752     
17753     show : function()
17754     {
17755         this.picker().show();
17756         this.pop.show();
17757         this.update();
17758         this.place();
17759         
17760         this.fireEvent('show', this, this.date);
17761     },
17762     
17763     hide : function()
17764     {
17765         this.picker().hide();
17766         this.pop.hide();
17767         
17768         this.fireEvent('hide', this, this.date);
17769     },
17770     
17771     setTime : function()
17772     {
17773         this.hide();
17774         this.setValue(this.time.format(this.format));
17775         
17776         this.fireEvent('select', this, this.date);
17777         
17778         
17779     },
17780     
17781     onMousedown: function(e){
17782         e.stopPropagation();
17783         e.preventDefault();
17784     },
17785     
17786     onIncrementHours: function()
17787     {
17788         Roo.log('onIncrementHours');
17789         this.time = this.time.add(Date.HOUR, 1);
17790         this.update();
17791         
17792     },
17793     
17794     onDecrementHours: function()
17795     {
17796         Roo.log('onDecrementHours');
17797         this.time = this.time.add(Date.HOUR, -1);
17798         this.update();
17799     },
17800     
17801     onIncrementMinutes: function()
17802     {
17803         Roo.log('onIncrementMinutes');
17804         this.time = this.time.add(Date.MINUTE, 1);
17805         this.update();
17806     },
17807     
17808     onDecrementMinutes: function()
17809     {
17810         Roo.log('onDecrementMinutes');
17811         this.time = this.time.add(Date.MINUTE, -1);
17812         this.update();
17813     },
17814     
17815     onTogglePeriod: function()
17816     {
17817         Roo.log('onTogglePeriod');
17818         this.time = this.time.add(Date.HOUR, 12);
17819         this.update();
17820     }
17821     
17822    
17823 });
17824
17825 Roo.apply(Roo.bootstrap.TimeField,  {
17826     
17827     content : {
17828         tag: 'tbody',
17829         cn: [
17830             {
17831                 tag: 'tr',
17832                 cn: [
17833                 {
17834                     tag: 'td',
17835                     colspan: '7'
17836                 }
17837                 ]
17838             }
17839         ]
17840     },
17841     
17842     footer : {
17843         tag: 'tfoot',
17844         cn: [
17845             {
17846                 tag: 'tr',
17847                 cn: [
17848                 {
17849                     tag: 'th',
17850                     colspan: '7',
17851                     cls: '',
17852                     cn: [
17853                         {
17854                             tag: 'button',
17855                             cls: 'btn btn-info ok',
17856                             html: 'OK'
17857                         }
17858                     ]
17859                 }
17860
17861                 ]
17862             }
17863         ]
17864     }
17865 });
17866
17867 Roo.apply(Roo.bootstrap.TimeField,  {
17868   
17869     template : {
17870         tag: 'div',
17871         cls: 'datepicker dropdown-menu',
17872         cn: [
17873             {
17874                 tag: 'div',
17875                 cls: 'datepicker-time',
17876                 cn: [
17877                 {
17878                     tag: 'table',
17879                     cls: 'table-condensed',
17880                     cn:[
17881                     Roo.bootstrap.TimeField.content,
17882                     Roo.bootstrap.TimeField.footer
17883                     ]
17884                 }
17885                 ]
17886             }
17887         ]
17888     }
17889 });
17890
17891  
17892
17893  /*
17894  * - LGPL
17895  *
17896  * MonthField
17897  * 
17898  */
17899
17900 /**
17901  * @class Roo.bootstrap.MonthField
17902  * @extends Roo.bootstrap.Input
17903  * Bootstrap MonthField class
17904  * 
17905  * @cfg {String} language default en
17906  * 
17907  * @constructor
17908  * Create a new MonthField
17909  * @param {Object} config The config object
17910  */
17911
17912 Roo.bootstrap.MonthField = function(config){
17913     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17914     
17915     this.addEvents({
17916         /**
17917          * @event show
17918          * Fires when this field show.
17919          * @param {Roo.bootstrap.MonthField} this
17920          * @param {Mixed} date The date value
17921          */
17922         show : true,
17923         /**
17924          * @event show
17925          * Fires when this field hide.
17926          * @param {Roo.bootstrap.MonthField} this
17927          * @param {Mixed} date The date value
17928          */
17929         hide : true,
17930         /**
17931          * @event select
17932          * Fires when select a date.
17933          * @param {Roo.bootstrap.MonthField} this
17934          * @param {String} oldvalue The old value
17935          * @param {String} newvalue The new value
17936          */
17937         select : true
17938     });
17939 };
17940
17941 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17942     
17943     onRender: function(ct, position)
17944     {
17945         
17946         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17947         
17948         this.language = this.language || 'en';
17949         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17950         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17951         
17952         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17953         this.isInline = false;
17954         this.isInput = true;
17955         this.component = this.el.select('.add-on', true).first() || false;
17956         this.component = (this.component && this.component.length === 0) ? false : this.component;
17957         this.hasInput = this.component && this.inputEL().length;
17958         
17959         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17960         
17961         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17962         
17963         this.picker().on('mousedown', this.onMousedown, this);
17964         this.picker().on('click', this.onClick, this);
17965         
17966         this.picker().addClass('datepicker-dropdown');
17967         
17968         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17969             v.setStyle('width', '189px');
17970         });
17971         
17972         this.fillMonths();
17973         
17974         this.update();
17975         
17976         if(this.isInline) {
17977             this.show();
17978         }
17979         
17980     },
17981     
17982     setValue: function(v, suppressEvent)
17983     {   
17984         var o = this.getValue();
17985         
17986         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17987         
17988         this.update();
17989
17990         if(suppressEvent !== true){
17991             this.fireEvent('select', this, o, v);
17992         }
17993         
17994     },
17995     
17996     getValue: function()
17997     {
17998         return this.value;
17999     },
18000     
18001     onClick: function(e) 
18002     {
18003         e.stopPropagation();
18004         e.preventDefault();
18005         
18006         var target = e.getTarget();
18007         
18008         if(target.nodeName.toLowerCase() === 'i'){
18009             target = Roo.get(target).dom.parentNode;
18010         }
18011         
18012         var nodeName = target.nodeName;
18013         var className = target.className;
18014         var html = target.innerHTML;
18015         
18016         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18017             return;
18018         }
18019         
18020         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18021         
18022         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18023         
18024         this.hide();
18025                         
18026     },
18027     
18028     picker : function()
18029     {
18030         return this.pickerEl;
18031     },
18032     
18033     fillMonths: function()
18034     {    
18035         var i = 0;
18036         var months = this.picker().select('>.datepicker-months td', true).first();
18037         
18038         months.dom.innerHTML = '';
18039         
18040         while (i < 12) {
18041             var month = {
18042                 tag: 'span',
18043                 cls: 'month',
18044                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18045             };
18046             
18047             months.createChild(month);
18048         }
18049         
18050     },
18051     
18052     update: function()
18053     {
18054         var _this = this;
18055         
18056         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18057             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18058         }
18059         
18060         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18061             e.removeClass('active');
18062             
18063             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18064                 e.addClass('active');
18065             }
18066         })
18067     },
18068     
18069     place: function()
18070     {
18071         if(this.isInline) return;
18072         
18073         this.picker().removeClass(['bottom', 'top']);
18074         
18075         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18076             /*
18077              * place to the top of element!
18078              *
18079              */
18080             
18081             this.picker().addClass('top');
18082             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18083             
18084             return;
18085         }
18086         
18087         this.picker().addClass('bottom');
18088         
18089         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18090     },
18091     
18092     onFocus : function()
18093     {
18094         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18095         this.show();
18096     },
18097     
18098     onBlur : function()
18099     {
18100         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18101         
18102         var d = this.inputEl().getValue();
18103         
18104         this.setValue(d);
18105                 
18106         this.hide();
18107     },
18108     
18109     show : function()
18110     {
18111         this.picker().show();
18112         this.picker().select('>.datepicker-months', true).first().show();
18113         this.update();
18114         this.place();
18115         
18116         this.fireEvent('show', this, this.date);
18117     },
18118     
18119     hide : function()
18120     {
18121         if(this.isInline) return;
18122         this.picker().hide();
18123         this.fireEvent('hide', this, this.date);
18124         
18125     },
18126     
18127     onMousedown: function(e)
18128     {
18129         e.stopPropagation();
18130         e.preventDefault();
18131     },
18132     
18133     keyup: function(e)
18134     {
18135         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18136         this.update();
18137     },
18138
18139     fireKey: function(e)
18140     {
18141         if (!this.picker().isVisible()){
18142             if (e.keyCode == 27) // allow escape to hide and re-show picker
18143                 this.show();
18144             return;
18145         }
18146         
18147         var dir;
18148         
18149         switch(e.keyCode){
18150             case 27: // escape
18151                 this.hide();
18152                 e.preventDefault();
18153                 break;
18154             case 37: // left
18155             case 39: // right
18156                 dir = e.keyCode == 37 ? -1 : 1;
18157                 
18158                 this.vIndex = this.vIndex + dir;
18159                 
18160                 if(this.vIndex < 0){
18161                     this.vIndex = 0;
18162                 }
18163                 
18164                 if(this.vIndex > 11){
18165                     this.vIndex = 11;
18166                 }
18167                 
18168                 if(isNaN(this.vIndex)){
18169                     this.vIndex = 0;
18170                 }
18171                 
18172                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18173                 
18174                 break;
18175             case 38: // up
18176             case 40: // down
18177                 
18178                 dir = e.keyCode == 38 ? -1 : 1;
18179                 
18180                 this.vIndex = this.vIndex + dir * 4;
18181                 
18182                 if(this.vIndex < 0){
18183                     this.vIndex = 0;
18184                 }
18185                 
18186                 if(this.vIndex > 11){
18187                     this.vIndex = 11;
18188                 }
18189                 
18190                 if(isNaN(this.vIndex)){
18191                     this.vIndex = 0;
18192                 }
18193                 
18194                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18195                 break;
18196                 
18197             case 13: // enter
18198                 
18199                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18200                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18201                 }
18202                 
18203                 this.hide();
18204                 e.preventDefault();
18205                 break;
18206             case 9: // tab
18207                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18208                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18209                 }
18210                 this.hide();
18211                 break;
18212             case 16: // shift
18213             case 17: // ctrl
18214             case 18: // alt
18215                 break;
18216             default :
18217                 this.hide();
18218                 
18219         }
18220     },
18221     
18222     remove: function() 
18223     {
18224         this.picker().remove();
18225     }
18226    
18227 });
18228
18229 Roo.apply(Roo.bootstrap.MonthField,  {
18230     
18231     content : {
18232         tag: 'tbody',
18233         cn: [
18234         {
18235             tag: 'tr',
18236             cn: [
18237             {
18238                 tag: 'td',
18239                 colspan: '7'
18240             }
18241             ]
18242         }
18243         ]
18244     },
18245     
18246     dates:{
18247         en: {
18248             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18249             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18250         }
18251     }
18252 });
18253
18254 Roo.apply(Roo.bootstrap.MonthField,  {
18255   
18256     template : {
18257         tag: 'div',
18258         cls: 'datepicker dropdown-menu roo-dynamic',
18259         cn: [
18260             {
18261                 tag: 'div',
18262                 cls: 'datepicker-months',
18263                 cn: [
18264                 {
18265                     tag: 'table',
18266                     cls: 'table-condensed',
18267                     cn:[
18268                         Roo.bootstrap.DateField.content
18269                     ]
18270                 }
18271                 ]
18272             }
18273         ]
18274     }
18275 });
18276
18277  
18278
18279  
18280  /*
18281  * - LGPL
18282  *
18283  * CheckBox
18284  * 
18285  */
18286
18287 /**
18288  * @class Roo.bootstrap.CheckBox
18289  * @extends Roo.bootstrap.Input
18290  * Bootstrap CheckBox class
18291  * 
18292  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18293  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18294  * @cfg {String} boxLabel The text that appears beside the checkbox
18295  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18296  * @cfg {Boolean} checked initnal the element
18297  * @cfg {Boolean} inline inline the element (default false)
18298  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18299  * 
18300  * @constructor
18301  * Create a new CheckBox
18302  * @param {Object} config The config object
18303  */
18304
18305 Roo.bootstrap.CheckBox = function(config){
18306     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18307    
18308     this.addEvents({
18309         /**
18310         * @event check
18311         * Fires when the element is checked or unchecked.
18312         * @param {Roo.bootstrap.CheckBox} this This input
18313         * @param {Boolean} checked The new checked value
18314         */
18315        check : true
18316     });
18317     
18318 };
18319
18320 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18321   
18322     inputType: 'checkbox',
18323     inputValue: 1,
18324     valueOff: 0,
18325     boxLabel: false,
18326     checked: false,
18327     weight : false,
18328     inline: false,
18329     
18330     getAutoCreate : function()
18331     {
18332         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18333         
18334         var id = Roo.id();
18335         
18336         var cfg = {};
18337         
18338         cfg.cls = 'form-group ' + this.inputType; //input-group
18339         
18340         if(this.inline){
18341             cfg.cls += ' ' + this.inputType + '-inline';
18342         }
18343         
18344         var input =  {
18345             tag: 'input',
18346             id : id,
18347             type : this.inputType,
18348             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18349             cls : 'roo-' + this.inputType, //'form-box',
18350             placeholder : this.placeholder || ''
18351             
18352         };
18353         
18354         if (this.weight) { // Validity check?
18355             cfg.cls += " " + this.inputType + "-" + this.weight;
18356         }
18357         
18358         if (this.disabled) {
18359             input.disabled=true;
18360         }
18361         
18362         if(this.checked){
18363             input.checked = this.checked;
18364         }
18365         
18366         if (this.name) {
18367             input.name = this.name;
18368         }
18369         
18370         if (this.size) {
18371             input.cls += ' input-' + this.size;
18372         }
18373         
18374         var settings=this;
18375         
18376         ['xs','sm','md','lg'].map(function(size){
18377             if (settings[size]) {
18378                 cfg.cls += ' col-' + size + '-' + settings[size];
18379             }
18380         });
18381         
18382         var inputblock = input;
18383          
18384         if (this.before || this.after) {
18385             
18386             inputblock = {
18387                 cls : 'input-group',
18388                 cn :  [] 
18389             };
18390             
18391             if (this.before) {
18392                 inputblock.cn.push({
18393                     tag :'span',
18394                     cls : 'input-group-addon',
18395                     html : this.before
18396                 });
18397             }
18398             
18399             inputblock.cn.push(input);
18400             
18401             if (this.after) {
18402                 inputblock.cn.push({
18403                     tag :'span',
18404                     cls : 'input-group-addon',
18405                     html : this.after
18406                 });
18407             }
18408             
18409         }
18410         
18411         if (align ==='left' && this.fieldLabel.length) {
18412                 Roo.log("left and has label");
18413                 cfg.cn = [
18414                     
18415                     {
18416                         tag: 'label',
18417                         'for' :  id,
18418                         cls : 'control-label col-md-' + this.labelWidth,
18419                         html : this.fieldLabel
18420                         
18421                     },
18422                     {
18423                         cls : "col-md-" + (12 - this.labelWidth), 
18424                         cn: [
18425                             inputblock
18426                         ]
18427                     }
18428                     
18429                 ];
18430         } else if ( this.fieldLabel.length) {
18431                 Roo.log(" label");
18432                 cfg.cn = [
18433                    
18434                     {
18435                         tag: this.boxLabel ? 'span' : 'label',
18436                         'for': id,
18437                         cls: 'control-label box-input-label',
18438                         //cls : 'input-group-addon',
18439                         html : this.fieldLabel
18440                         
18441                     },
18442                     
18443                     inputblock
18444                     
18445                 ];
18446
18447         } else {
18448             
18449                 Roo.log(" no label && no align");
18450                 cfg.cn = [  inputblock ] ;
18451                 
18452                 
18453         }
18454         if(this.boxLabel){
18455              var boxLabelCfg = {
18456                 tag: 'label',
18457                 //'for': id, // box label is handled by onclick - so no for...
18458                 cls: 'box-label',
18459                 html: this.boxLabel
18460             };
18461             
18462             if(this.tooltip){
18463                 boxLabelCfg.tooltip = this.tooltip;
18464             }
18465              
18466             cfg.cn.push(boxLabelCfg);
18467         }
18468         
18469         
18470        
18471         return cfg;
18472         
18473     },
18474     
18475     /**
18476      * return the real input element.
18477      */
18478     inputEl: function ()
18479     {
18480         return this.el.select('input.roo-' + this.inputType,true).first();
18481     },
18482     
18483     labelEl: function()
18484     {
18485         return this.el.select('label.control-label',true).first();
18486     },
18487     /* depricated... */
18488     
18489     label: function()
18490     {
18491         return this.labelEl();
18492     },
18493     
18494     initEvents : function()
18495     {
18496 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18497         
18498         this.inputEl().on('click', this.onClick,  this);
18499         
18500         if (this.boxLabel) { 
18501             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18502         }
18503         
18504         this.startValue = this.getValue();
18505         
18506         if(this.groupId){
18507             Roo.bootstrap.CheckBox.register(this);
18508         }
18509     },
18510     
18511     onClick : function()
18512     {   
18513         this.setChecked(!this.checked);
18514     },
18515     
18516     setChecked : function(state,suppressEvent)
18517     {
18518         this.startValue = this.getValue();
18519         
18520         if(this.inputType == 'radio'){
18521             
18522             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18523                 e.dom.checked = false;
18524             });
18525             
18526             this.inputEl().dom.checked = true;
18527             
18528             this.inputEl().dom.value = this.inputValue;
18529             
18530             if(suppressEvent !== true){
18531                 this.fireEvent('check', this, true);
18532             }
18533             
18534             this.validate();
18535             
18536             return;
18537         }
18538         
18539         this.checked = state;
18540         
18541         this.inputEl().dom.checked = state;
18542         
18543         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18544         
18545         if(suppressEvent !== true){
18546             this.fireEvent('check', this, state);
18547         }
18548         
18549         this.validate();
18550     },
18551     
18552     getValue : function()
18553     {
18554         if(this.inputType == 'radio'){
18555             return this.getGroupValue();
18556         }
18557         
18558         return this.inputEl().getValue();
18559         
18560     },
18561     
18562     getGroupValue : function()
18563     {
18564         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18565             return '';
18566         }
18567         
18568         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18569     },
18570     
18571     setValue : function(v,suppressEvent)
18572     {
18573         if(this.inputType == 'radio'){
18574             this.setGroupValue(v, suppressEvent);
18575             return;
18576         }
18577         
18578         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18579         
18580         this.validate();
18581     },
18582     
18583     setGroupValue : function(v, suppressEvent)
18584     {
18585         this.startValue = this.getValue();
18586         
18587         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18588             e.dom.checked = false;
18589             
18590             if(e.dom.value == v){
18591                 e.dom.checked = true;
18592             }
18593         });
18594         
18595         if(suppressEvent !== true){
18596             this.fireEvent('check', this, true);
18597         }
18598
18599         this.validate();
18600         
18601         return;
18602     },
18603     
18604     validate : function()
18605     {
18606         if(
18607                 this.disabled || 
18608                 (this.inputType == 'radio' && this.validateRadio()) ||
18609                 (this.inputType == 'checkbox' && this.validateCheckbox())
18610         ){
18611             this.markValid();
18612             return true;
18613         }
18614         
18615         this.markInvalid();
18616         return false;
18617     },
18618     
18619     validateRadio : function()
18620     {
18621         var valid = false;
18622         
18623         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18624             if(!e.dom.checked){
18625                 return;
18626             }
18627             
18628             valid = true;
18629             
18630             return false;
18631         });
18632         
18633         return valid;
18634     },
18635     
18636     validateCheckbox : function()
18637     {
18638         if(!this.groupId){
18639             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18640         }
18641         
18642         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18643         
18644         if(!group){
18645             return false;
18646         }
18647         
18648         var r = false;
18649         
18650         for(var i in group){
18651             if(r){
18652                 break;
18653             }
18654             
18655             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18656         }
18657         
18658         return r;
18659     },
18660     
18661     /**
18662      * Mark this field as valid
18663      */
18664     markValid : function()
18665     {
18666         if(this.allowBlank){
18667             return;
18668         }
18669         
18670         var _this = this;
18671         
18672         this.fireEvent('valid', this);
18673         
18674         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18675         
18676         if(this.groupId){
18677             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18678         }
18679         
18680         if(label){
18681             label.markValid();
18682         }
18683         
18684         if(this.inputType == 'radio'){
18685             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18686                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18687                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18688             });
18689             
18690             return;
18691         }
18692         
18693         if(!this.groupId){
18694             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18695             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18696             return;
18697         }
18698         
18699         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18700             
18701         if(!group){
18702             return;
18703         }
18704         
18705         for(var i in group){
18706             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18707             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18708         }
18709     },
18710     
18711      /**
18712      * Mark this field as invalid
18713      * @param {String} msg The validation message
18714      */
18715     markInvalid : function(msg)
18716     {
18717         if(this.allowBlank){
18718             return;
18719         }
18720         
18721         var _this = this;
18722         
18723         this.fireEvent('invalid', this, msg);
18724         
18725         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18726         
18727         if(this.groupId){
18728             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18729         }
18730         
18731         if(label){
18732             label.markInvalid();
18733         }
18734             
18735         if(this.inputType == 'radio'){
18736             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18737                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18738                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18739             });
18740             
18741             return;
18742         }
18743         
18744         if(!this.groupId){
18745             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18746             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18747             return;
18748         }
18749         
18750         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18751         
18752         if(!group){
18753             return;
18754         }
18755         
18756         for(var i in group){
18757             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18758             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18759         }
18760         
18761     }
18762     
18763 });
18764
18765 Roo.apply(Roo.bootstrap.CheckBox, {
18766     
18767     groups: {},
18768     
18769      /**
18770     * register a CheckBox Group
18771     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18772     */
18773     register : function(checkbox)
18774     {
18775         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18776             this.groups[checkbox.groupId] = {};
18777         }
18778         
18779         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18780             return;
18781         }
18782         
18783         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18784         
18785     },
18786     /**
18787     * fetch a CheckBox Group based on the group ID
18788     * @param {string} the group ID
18789     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18790     */
18791     get: function(groupId) {
18792         if (typeof(this.groups[groupId]) == 'undefined') {
18793             return false;
18794         }
18795         
18796         return this.groups[groupId] ;
18797     }
18798     
18799     
18800 });
18801 /*
18802  * - LGPL
18803  *
18804  * Radio
18805  *
18806  *
18807  * not inline
18808  *<div class="radio">
18809   <label>
18810     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18811     Option one is this and that&mdash;be sure to include why it's great
18812   </label>
18813 </div>
18814  *
18815  *
18816  *inline
18817  *<span>
18818  *<label class="radio-inline">fieldLabel</label>
18819  *<label class="radio-inline">
18820   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18821 </label>
18822 <span>
18823  * 
18824  * 
18825  */
18826
18827 /**
18828  * @class Roo.bootstrap.Radio
18829  * @extends Roo.bootstrap.CheckBox
18830  * Bootstrap Radio class
18831
18832  * @constructor
18833  * Create a new Radio
18834  * @param {Object} config The config object
18835  */
18836
18837 Roo.bootstrap.Radio = function(config){
18838     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18839    
18840 };
18841
18842 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18843     
18844     inputType: 'radio',
18845     inputValue: '',
18846     valueOff: '',
18847     
18848     getAutoCreate : function()
18849     {
18850         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18851         align = align || 'left'; // default...
18852         
18853         
18854         
18855         var id = Roo.id();
18856         
18857         var cfg = {
18858                 tag : this.inline ? 'span' : 'div',
18859                 cls : '',
18860                 cn : []
18861         };
18862         
18863         var inline = this.inline ? ' radio-inline' : '';
18864         
18865         var lbl = {
18866                 tag: 'label' ,
18867                 // does not need for, as we wrap the input with it..
18868                 'for' : id,
18869                 cls : 'control-label box-label' + inline,
18870                 cn : []
18871         };
18872         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18873         
18874         var fieldLabel = {
18875             tag: 'label' ,
18876             //cls : 'control-label' + inline,
18877             html : this.fieldLabel,
18878             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18879         };
18880         
18881  
18882         
18883         
18884         var input =  {
18885             tag: 'input',
18886             id : id,
18887             type : this.inputType,
18888             //value : (!this.checked) ? this.valueOff : this.inputValue,
18889             value : this.inputValue,
18890             cls : 'roo-radio',
18891             placeholder : this.placeholder || '' // ?? needed????
18892             
18893         };
18894         if (this.weight) { // Validity check?
18895             input.cls += " radio-" + this.weight;
18896         }
18897         if (this.disabled) {
18898             input.disabled=true;
18899         }
18900         
18901         if(this.checked){
18902             input.checked = this.checked;
18903         }
18904         
18905         if (this.name) {
18906             input.name = this.name;
18907         }
18908         
18909         if (this.size) {
18910             input.cls += ' input-' + this.size;
18911         }
18912         
18913         //?? can span's inline have a width??
18914         
18915         var settings=this;
18916         ['xs','sm','md','lg'].map(function(size){
18917             if (settings[size]) {
18918                 cfg.cls += ' col-' + size + '-' + settings[size];
18919             }
18920         });
18921         
18922         var inputblock = input;
18923         
18924         if (this.before || this.after) {
18925             
18926             inputblock = {
18927                 cls : 'input-group',
18928                 tag : 'span',
18929                 cn :  [] 
18930             };
18931             if (this.before) {
18932                 inputblock.cn.push({
18933                     tag :'span',
18934                     cls : 'input-group-addon',
18935                     html : this.before
18936                 });
18937             }
18938             inputblock.cn.push(input);
18939             if (this.after) {
18940                 inputblock.cn.push({
18941                     tag :'span',
18942                     cls : 'input-group-addon',
18943                     html : this.after
18944                 });
18945             }
18946             
18947         };
18948         
18949         
18950         if (this.fieldLabel && this.fieldLabel.length) {
18951             cfg.cn.push(fieldLabel);
18952         }
18953        
18954         // normal bootstrap puts the input inside the label.
18955         // however with our styled version - it has to go after the input.
18956        
18957         //lbl.cn.push(inputblock);
18958         
18959         var lblwrap =  {
18960             tag: 'span',
18961             cls: 'radio' + inline,
18962             cn: [
18963                 inputblock,
18964                 lbl
18965             ]
18966         };
18967         
18968         cfg.cn.push( lblwrap);
18969         
18970         if(this.boxLabel){
18971             lbl.cn.push({
18972                 tag: 'span',
18973                 html: this.boxLabel
18974             })
18975         }
18976          
18977         
18978         return cfg;
18979         
18980     },
18981     
18982     initEvents : function()
18983     {
18984 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18985         
18986         this.inputEl().on('click', this.onClick,  this);
18987         if (this.boxLabel) {
18988             Roo.log('find label')
18989             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18990         }
18991         
18992     },
18993     
18994     inputEl: function ()
18995     {
18996         return this.el.select('input.roo-radio',true).first();
18997     },
18998     onClick : function()
18999     {   
19000         Roo.log("click");
19001         this.setChecked(true);
19002     },
19003     
19004     setChecked : function(state,suppressEvent)
19005     {
19006         if(state){
19007             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19008                 v.dom.checked = false;
19009             });
19010         }
19011         Roo.log(this.inputEl().dom);
19012         this.checked = state;
19013         this.inputEl().dom.checked = state;
19014         
19015         if(suppressEvent !== true){
19016             this.fireEvent('check', this, state);
19017         }
19018         
19019         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19020         
19021     },
19022     
19023     getGroupValue : function()
19024     {
19025         var value = '';
19026         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19027             if(v.dom.checked == true){
19028                 value = v.dom.value;
19029             }
19030         });
19031         
19032         return value;
19033     },
19034     
19035     /**
19036      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19037      * @return {Mixed} value The field value
19038      */
19039     getValue : function(){
19040         return this.getGroupValue();
19041     }
19042     
19043 });
19044
19045  
19046 //<script type="text/javascript">
19047
19048 /*
19049  * Based  Ext JS Library 1.1.1
19050  * Copyright(c) 2006-2007, Ext JS, LLC.
19051  * LGPL
19052  *
19053  */
19054  
19055 /**
19056  * @class Roo.HtmlEditorCore
19057  * @extends Roo.Component
19058  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19059  *
19060  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19061  */
19062
19063 Roo.HtmlEditorCore = function(config){
19064     
19065     
19066     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19067     
19068     
19069     this.addEvents({
19070         /**
19071          * @event initialize
19072          * Fires when the editor is fully initialized (including the iframe)
19073          * @param {Roo.HtmlEditorCore} this
19074          */
19075         initialize: true,
19076         /**
19077          * @event activate
19078          * Fires when the editor is first receives the focus. Any insertion must wait
19079          * until after this event.
19080          * @param {Roo.HtmlEditorCore} this
19081          */
19082         activate: true,
19083          /**
19084          * @event beforesync
19085          * Fires before the textarea is updated with content from the editor iframe. Return false
19086          * to cancel the sync.
19087          * @param {Roo.HtmlEditorCore} this
19088          * @param {String} html
19089          */
19090         beforesync: true,
19091          /**
19092          * @event beforepush
19093          * Fires before the iframe editor is updated with content from the textarea. Return false
19094          * to cancel the push.
19095          * @param {Roo.HtmlEditorCore} this
19096          * @param {String} html
19097          */
19098         beforepush: true,
19099          /**
19100          * @event sync
19101          * Fires when the textarea is updated with content from the editor iframe.
19102          * @param {Roo.HtmlEditorCore} this
19103          * @param {String} html
19104          */
19105         sync: true,
19106          /**
19107          * @event push
19108          * Fires when the iframe editor is updated with content from the textarea.
19109          * @param {Roo.HtmlEditorCore} this
19110          * @param {String} html
19111          */
19112         push: true,
19113         
19114         /**
19115          * @event editorevent
19116          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19117          * @param {Roo.HtmlEditorCore} this
19118          */
19119         editorevent: true
19120         
19121     });
19122     
19123     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19124     
19125     // defaults : white / black...
19126     this.applyBlacklists();
19127     
19128     
19129     
19130 };
19131
19132
19133 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19134
19135
19136      /**
19137      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19138      */
19139     
19140     owner : false,
19141     
19142      /**
19143      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19144      *                        Roo.resizable.
19145      */
19146     resizable : false,
19147      /**
19148      * @cfg {Number} height (in pixels)
19149      */   
19150     height: 300,
19151    /**
19152      * @cfg {Number} width (in pixels)
19153      */   
19154     width: 500,
19155     
19156     /**
19157      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19158      * 
19159      */
19160     stylesheets: false,
19161     
19162     // id of frame..
19163     frameId: false,
19164     
19165     // private properties
19166     validationEvent : false,
19167     deferHeight: true,
19168     initialized : false,
19169     activated : false,
19170     sourceEditMode : false,
19171     onFocus : Roo.emptyFn,
19172     iframePad:3,
19173     hideMode:'offsets',
19174     
19175     clearUp: true,
19176     
19177     // blacklist + whitelisted elements..
19178     black: false,
19179     white: false,
19180      
19181     
19182
19183     /**
19184      * Protected method that will not generally be called directly. It
19185      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19186      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19187      */
19188     getDocMarkup : function(){
19189         // body styles..
19190         var st = '';
19191         
19192         // inherit styels from page...?? 
19193         if (this.stylesheets === false) {
19194             
19195             Roo.get(document.head).select('style').each(function(node) {
19196                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19197             });
19198             
19199             Roo.get(document.head).select('link').each(function(node) { 
19200                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19201             });
19202             
19203         } else if (!this.stylesheets.length) {
19204                 // simple..
19205                 st = '<style type="text/css">' +
19206                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19207                    '</style>';
19208         } else { 
19209             
19210         }
19211         
19212         st +=  '<style type="text/css">' +
19213             'IMG { cursor: pointer } ' +
19214         '</style>';
19215
19216         
19217         return '<html><head>' + st  +
19218             //<style type="text/css">' +
19219             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19220             //'</style>' +
19221             ' </head><body class="roo-htmleditor-body"></body></html>';
19222     },
19223
19224     // private
19225     onRender : function(ct, position)
19226     {
19227         var _t = this;
19228         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19229         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19230         
19231         
19232         this.el.dom.style.border = '0 none';
19233         this.el.dom.setAttribute('tabIndex', -1);
19234         this.el.addClass('x-hidden hide');
19235         
19236         
19237         
19238         if(Roo.isIE){ // fix IE 1px bogus margin
19239             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19240         }
19241        
19242         
19243         this.frameId = Roo.id();
19244         
19245          
19246         
19247         var iframe = this.owner.wrap.createChild({
19248             tag: 'iframe',
19249             cls: 'form-control', // bootstrap..
19250             id: this.frameId,
19251             name: this.frameId,
19252             frameBorder : 'no',
19253             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19254         }, this.el
19255         );
19256         
19257         
19258         this.iframe = iframe.dom;
19259
19260          this.assignDocWin();
19261         
19262         this.doc.designMode = 'on';
19263        
19264         this.doc.open();
19265         this.doc.write(this.getDocMarkup());
19266         this.doc.close();
19267
19268         
19269         var task = { // must defer to wait for browser to be ready
19270             run : function(){
19271                 //console.log("run task?" + this.doc.readyState);
19272                 this.assignDocWin();
19273                 if(this.doc.body || this.doc.readyState == 'complete'){
19274                     try {
19275                         this.doc.designMode="on";
19276                     } catch (e) {
19277                         return;
19278                     }
19279                     Roo.TaskMgr.stop(task);
19280                     this.initEditor.defer(10, this);
19281                 }
19282             },
19283             interval : 10,
19284             duration: 10000,
19285             scope: this
19286         };
19287         Roo.TaskMgr.start(task);
19288
19289     },
19290
19291     // private
19292     onResize : function(w, h)
19293     {
19294          Roo.log('resize: ' +w + ',' + h );
19295         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19296         if(!this.iframe){
19297             return;
19298         }
19299         if(typeof w == 'number'){
19300             
19301             this.iframe.style.width = w + 'px';
19302         }
19303         if(typeof h == 'number'){
19304             
19305             this.iframe.style.height = h + 'px';
19306             if(this.doc){
19307                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19308             }
19309         }
19310         
19311     },
19312
19313     /**
19314      * Toggles the editor between standard and source edit mode.
19315      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19316      */
19317     toggleSourceEdit : function(sourceEditMode){
19318         
19319         this.sourceEditMode = sourceEditMode === true;
19320         
19321         if(this.sourceEditMode){
19322  
19323             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19324             
19325         }else{
19326             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19327             //this.iframe.className = '';
19328             this.deferFocus();
19329         }
19330         //this.setSize(this.owner.wrap.getSize());
19331         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19332     },
19333
19334     
19335   
19336
19337     /**
19338      * Protected method that will not generally be called directly. If you need/want
19339      * custom HTML cleanup, this is the method you should override.
19340      * @param {String} html The HTML to be cleaned
19341      * return {String} The cleaned HTML
19342      */
19343     cleanHtml : function(html){
19344         html = String(html);
19345         if(html.length > 5){
19346             if(Roo.isSafari){ // strip safari nonsense
19347                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19348             }
19349         }
19350         if(html == '&nbsp;'){
19351             html = '';
19352         }
19353         return html;
19354     },
19355
19356     /**
19357      * HTML Editor -> Textarea
19358      * Protected method that will not generally be called directly. Syncs the contents
19359      * of the editor iframe with the textarea.
19360      */
19361     syncValue : function(){
19362         if(this.initialized){
19363             var bd = (this.doc.body || this.doc.documentElement);
19364             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19365             var html = bd.innerHTML;
19366             if(Roo.isSafari){
19367                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19368                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19369                 if(m && m[1]){
19370                     html = '<div style="'+m[0]+'">' + html + '</div>';
19371                 }
19372             }
19373             html = this.cleanHtml(html);
19374             // fix up the special chars.. normaly like back quotes in word...
19375             // however we do not want to do this with chinese..
19376             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19377                 var cc = b.charCodeAt();
19378                 if (
19379                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19380                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19381                     (cc >= 0xf900 && cc < 0xfb00 )
19382                 ) {
19383                         return b;
19384                 }
19385                 return "&#"+cc+";" 
19386             });
19387             if(this.owner.fireEvent('beforesync', this, html) !== false){
19388                 this.el.dom.value = html;
19389                 this.owner.fireEvent('sync', this, html);
19390             }
19391         }
19392     },
19393
19394     /**
19395      * Protected method that will not generally be called directly. Pushes the value of the textarea
19396      * into the iframe editor.
19397      */
19398     pushValue : function(){
19399         if(this.initialized){
19400             var v = this.el.dom.value.trim();
19401             
19402 //            if(v.length < 1){
19403 //                v = '&#160;';
19404 //            }
19405             
19406             if(this.owner.fireEvent('beforepush', this, v) !== false){
19407                 var d = (this.doc.body || this.doc.documentElement);
19408                 d.innerHTML = v;
19409                 this.cleanUpPaste();
19410                 this.el.dom.value = d.innerHTML;
19411                 this.owner.fireEvent('push', this, v);
19412             }
19413         }
19414     },
19415
19416     // private
19417     deferFocus : function(){
19418         this.focus.defer(10, this);
19419     },
19420
19421     // doc'ed in Field
19422     focus : function(){
19423         if(this.win && !this.sourceEditMode){
19424             this.win.focus();
19425         }else{
19426             this.el.focus();
19427         }
19428     },
19429     
19430     assignDocWin: function()
19431     {
19432         var iframe = this.iframe;
19433         
19434          if(Roo.isIE){
19435             this.doc = iframe.contentWindow.document;
19436             this.win = iframe.contentWindow;
19437         } else {
19438 //            if (!Roo.get(this.frameId)) {
19439 //                return;
19440 //            }
19441 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19442 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19443             
19444             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19445                 return;
19446             }
19447             
19448             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19449             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19450         }
19451     },
19452     
19453     // private
19454     initEditor : function(){
19455         //console.log("INIT EDITOR");
19456         this.assignDocWin();
19457         
19458         
19459         
19460         this.doc.designMode="on";
19461         this.doc.open();
19462         this.doc.write(this.getDocMarkup());
19463         this.doc.close();
19464         
19465         var dbody = (this.doc.body || this.doc.documentElement);
19466         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19467         // this copies styles from the containing element into thsi one..
19468         // not sure why we need all of this..
19469         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19470         
19471         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19472         //ss['background-attachment'] = 'fixed'; // w3c
19473         dbody.bgProperties = 'fixed'; // ie
19474         //Roo.DomHelper.applyStyles(dbody, ss);
19475         Roo.EventManager.on(this.doc, {
19476             //'mousedown': this.onEditorEvent,
19477             'mouseup': this.onEditorEvent,
19478             'dblclick': this.onEditorEvent,
19479             'click': this.onEditorEvent,
19480             'keyup': this.onEditorEvent,
19481             buffer:100,
19482             scope: this
19483         });
19484         if(Roo.isGecko){
19485             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19486         }
19487         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19488             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19489         }
19490         this.initialized = true;
19491
19492         this.owner.fireEvent('initialize', this);
19493         this.pushValue();
19494     },
19495
19496     // private
19497     onDestroy : function(){
19498         
19499         
19500         
19501         if(this.rendered){
19502             
19503             //for (var i =0; i < this.toolbars.length;i++) {
19504             //    // fixme - ask toolbars for heights?
19505             //    this.toolbars[i].onDestroy();
19506            // }
19507             
19508             //this.wrap.dom.innerHTML = '';
19509             //this.wrap.remove();
19510         }
19511     },
19512
19513     // private
19514     onFirstFocus : function(){
19515         
19516         this.assignDocWin();
19517         
19518         
19519         this.activated = true;
19520          
19521     
19522         if(Roo.isGecko){ // prevent silly gecko errors
19523             this.win.focus();
19524             var s = this.win.getSelection();
19525             if(!s.focusNode || s.focusNode.nodeType != 3){
19526                 var r = s.getRangeAt(0);
19527                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19528                 r.collapse(true);
19529                 this.deferFocus();
19530             }
19531             try{
19532                 this.execCmd('useCSS', true);
19533                 this.execCmd('styleWithCSS', false);
19534             }catch(e){}
19535         }
19536         this.owner.fireEvent('activate', this);
19537     },
19538
19539     // private
19540     adjustFont: function(btn){
19541         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19542         //if(Roo.isSafari){ // safari
19543         //    adjust *= 2;
19544        // }
19545         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19546         if(Roo.isSafari){ // safari
19547             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19548             v =  (v < 10) ? 10 : v;
19549             v =  (v > 48) ? 48 : v;
19550             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19551             
19552         }
19553         
19554         
19555         v = Math.max(1, v+adjust);
19556         
19557         this.execCmd('FontSize', v  );
19558     },
19559
19560     onEditorEvent : function(e)
19561     {
19562         this.owner.fireEvent('editorevent', this, e);
19563       //  this.updateToolbar();
19564         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19565     },
19566
19567     insertTag : function(tg)
19568     {
19569         // could be a bit smarter... -> wrap the current selected tRoo..
19570         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19571             
19572             range = this.createRange(this.getSelection());
19573             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19574             wrappingNode.appendChild(range.extractContents());
19575             range.insertNode(wrappingNode);
19576
19577             return;
19578             
19579             
19580             
19581         }
19582         this.execCmd("formatblock",   tg);
19583         
19584     },
19585     
19586     insertText : function(txt)
19587     {
19588         
19589         
19590         var range = this.createRange();
19591         range.deleteContents();
19592                //alert(Sender.getAttribute('label'));
19593                
19594         range.insertNode(this.doc.createTextNode(txt));
19595     } ,
19596     
19597      
19598
19599     /**
19600      * Executes a Midas editor command on the editor document and performs necessary focus and
19601      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19602      * @param {String} cmd The Midas command
19603      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19604      */
19605     relayCmd : function(cmd, value){
19606         this.win.focus();
19607         this.execCmd(cmd, value);
19608         this.owner.fireEvent('editorevent', this);
19609         //this.updateToolbar();
19610         this.owner.deferFocus();
19611     },
19612
19613     /**
19614      * Executes a Midas editor command directly on the editor document.
19615      * For visual commands, you should use {@link #relayCmd} instead.
19616      * <b>This should only be called after the editor is initialized.</b>
19617      * @param {String} cmd The Midas command
19618      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19619      */
19620     execCmd : function(cmd, value){
19621         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19622         this.syncValue();
19623     },
19624  
19625  
19626    
19627     /**
19628      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19629      * to insert tRoo.
19630      * @param {String} text | dom node.. 
19631      */
19632     insertAtCursor : function(text)
19633     {
19634         
19635         
19636         
19637         if(!this.activated){
19638             return;
19639         }
19640         /*
19641         if(Roo.isIE){
19642             this.win.focus();
19643             var r = this.doc.selection.createRange();
19644             if(r){
19645                 r.collapse(true);
19646                 r.pasteHTML(text);
19647                 this.syncValue();
19648                 this.deferFocus();
19649             
19650             }
19651             return;
19652         }
19653         */
19654         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19655             this.win.focus();
19656             
19657             
19658             // from jquery ui (MIT licenced)
19659             var range, node;
19660             var win = this.win;
19661             
19662             if (win.getSelection && win.getSelection().getRangeAt) {
19663                 range = win.getSelection().getRangeAt(0);
19664                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19665                 range.insertNode(node);
19666             } else if (win.document.selection && win.document.selection.createRange) {
19667                 // no firefox support
19668                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19669                 win.document.selection.createRange().pasteHTML(txt);
19670             } else {
19671                 // no firefox support
19672                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19673                 this.execCmd('InsertHTML', txt);
19674             } 
19675             
19676             this.syncValue();
19677             
19678             this.deferFocus();
19679         }
19680     },
19681  // private
19682     mozKeyPress : function(e){
19683         if(e.ctrlKey){
19684             var c = e.getCharCode(), cmd;
19685           
19686             if(c > 0){
19687                 c = String.fromCharCode(c).toLowerCase();
19688                 switch(c){
19689                     case 'b':
19690                         cmd = 'bold';
19691                         break;
19692                     case 'i':
19693                         cmd = 'italic';
19694                         break;
19695                     
19696                     case 'u':
19697                         cmd = 'underline';
19698                         break;
19699                     
19700                     case 'v':
19701                         this.cleanUpPaste.defer(100, this);
19702                         return;
19703                         
19704                 }
19705                 if(cmd){
19706                     this.win.focus();
19707                     this.execCmd(cmd);
19708                     this.deferFocus();
19709                     e.preventDefault();
19710                 }
19711                 
19712             }
19713         }
19714     },
19715
19716     // private
19717     fixKeys : function(){ // load time branching for fastest keydown performance
19718         if(Roo.isIE){
19719             return function(e){
19720                 var k = e.getKey(), r;
19721                 if(k == e.TAB){
19722                     e.stopEvent();
19723                     r = this.doc.selection.createRange();
19724                     if(r){
19725                         r.collapse(true);
19726                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19727                         this.deferFocus();
19728                     }
19729                     return;
19730                 }
19731                 
19732                 if(k == e.ENTER){
19733                     r = this.doc.selection.createRange();
19734                     if(r){
19735                         var target = r.parentElement();
19736                         if(!target || target.tagName.toLowerCase() != 'li'){
19737                             e.stopEvent();
19738                             r.pasteHTML('<br />');
19739                             r.collapse(false);
19740                             r.select();
19741                         }
19742                     }
19743                 }
19744                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19745                     this.cleanUpPaste.defer(100, this);
19746                     return;
19747                 }
19748                 
19749                 
19750             };
19751         }else if(Roo.isOpera){
19752             return function(e){
19753                 var k = e.getKey();
19754                 if(k == e.TAB){
19755                     e.stopEvent();
19756                     this.win.focus();
19757                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19758                     this.deferFocus();
19759                 }
19760                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19761                     this.cleanUpPaste.defer(100, this);
19762                     return;
19763                 }
19764                 
19765             };
19766         }else if(Roo.isSafari){
19767             return function(e){
19768                 var k = e.getKey();
19769                 
19770                 if(k == e.TAB){
19771                     e.stopEvent();
19772                     this.execCmd('InsertText','\t');
19773                     this.deferFocus();
19774                     return;
19775                 }
19776                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19777                     this.cleanUpPaste.defer(100, this);
19778                     return;
19779                 }
19780                 
19781              };
19782         }
19783     }(),
19784     
19785     getAllAncestors: function()
19786     {
19787         var p = this.getSelectedNode();
19788         var a = [];
19789         if (!p) {
19790             a.push(p); // push blank onto stack..
19791             p = this.getParentElement();
19792         }
19793         
19794         
19795         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19796             a.push(p);
19797             p = p.parentNode;
19798         }
19799         a.push(this.doc.body);
19800         return a;
19801     },
19802     lastSel : false,
19803     lastSelNode : false,
19804     
19805     
19806     getSelection : function() 
19807     {
19808         this.assignDocWin();
19809         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19810     },
19811     
19812     getSelectedNode: function() 
19813     {
19814         // this may only work on Gecko!!!
19815         
19816         // should we cache this!!!!
19817         
19818         
19819         
19820          
19821         var range = this.createRange(this.getSelection()).cloneRange();
19822         
19823         if (Roo.isIE) {
19824             var parent = range.parentElement();
19825             while (true) {
19826                 var testRange = range.duplicate();
19827                 testRange.moveToElementText(parent);
19828                 if (testRange.inRange(range)) {
19829                     break;
19830                 }
19831                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19832                     break;
19833                 }
19834                 parent = parent.parentElement;
19835             }
19836             return parent;
19837         }
19838         
19839         // is ancestor a text element.
19840         var ac =  range.commonAncestorContainer;
19841         if (ac.nodeType == 3) {
19842             ac = ac.parentNode;
19843         }
19844         
19845         var ar = ac.childNodes;
19846          
19847         var nodes = [];
19848         var other_nodes = [];
19849         var has_other_nodes = false;
19850         for (var i=0;i<ar.length;i++) {
19851             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19852                 continue;
19853             }
19854             // fullly contained node.
19855             
19856             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19857                 nodes.push(ar[i]);
19858                 continue;
19859             }
19860             
19861             // probably selected..
19862             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19863                 other_nodes.push(ar[i]);
19864                 continue;
19865             }
19866             // outer..
19867             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19868                 continue;
19869             }
19870             
19871             
19872             has_other_nodes = true;
19873         }
19874         if (!nodes.length && other_nodes.length) {
19875             nodes= other_nodes;
19876         }
19877         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19878             return false;
19879         }
19880         
19881         return nodes[0];
19882     },
19883     createRange: function(sel)
19884     {
19885         // this has strange effects when using with 
19886         // top toolbar - not sure if it's a great idea.
19887         //this.editor.contentWindow.focus();
19888         if (typeof sel != "undefined") {
19889             try {
19890                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19891             } catch(e) {
19892                 return this.doc.createRange();
19893             }
19894         } else {
19895             return this.doc.createRange();
19896         }
19897     },
19898     getParentElement: function()
19899     {
19900         
19901         this.assignDocWin();
19902         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19903         
19904         var range = this.createRange(sel);
19905          
19906         try {
19907             var p = range.commonAncestorContainer;
19908             while (p.nodeType == 3) { // text node
19909                 p = p.parentNode;
19910             }
19911             return p;
19912         } catch (e) {
19913             return null;
19914         }
19915     
19916     },
19917     /***
19918      *
19919      * Range intersection.. the hard stuff...
19920      *  '-1' = before
19921      *  '0' = hits..
19922      *  '1' = after.
19923      *         [ -- selected range --- ]
19924      *   [fail]                        [fail]
19925      *
19926      *    basically..
19927      *      if end is before start or  hits it. fail.
19928      *      if start is after end or hits it fail.
19929      *
19930      *   if either hits (but other is outside. - then it's not 
19931      *   
19932      *    
19933      **/
19934     
19935     
19936     // @see http://www.thismuchiknow.co.uk/?p=64.
19937     rangeIntersectsNode : function(range, node)
19938     {
19939         var nodeRange = node.ownerDocument.createRange();
19940         try {
19941             nodeRange.selectNode(node);
19942         } catch (e) {
19943             nodeRange.selectNodeContents(node);
19944         }
19945     
19946         var rangeStartRange = range.cloneRange();
19947         rangeStartRange.collapse(true);
19948     
19949         var rangeEndRange = range.cloneRange();
19950         rangeEndRange.collapse(false);
19951     
19952         var nodeStartRange = nodeRange.cloneRange();
19953         nodeStartRange.collapse(true);
19954     
19955         var nodeEndRange = nodeRange.cloneRange();
19956         nodeEndRange.collapse(false);
19957     
19958         return rangeStartRange.compareBoundaryPoints(
19959                  Range.START_TO_START, nodeEndRange) == -1 &&
19960                rangeEndRange.compareBoundaryPoints(
19961                  Range.START_TO_START, nodeStartRange) == 1;
19962         
19963          
19964     },
19965     rangeCompareNode : function(range, node)
19966     {
19967         var nodeRange = node.ownerDocument.createRange();
19968         try {
19969             nodeRange.selectNode(node);
19970         } catch (e) {
19971             nodeRange.selectNodeContents(node);
19972         }
19973         
19974         
19975         range.collapse(true);
19976     
19977         nodeRange.collapse(true);
19978      
19979         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19980         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19981          
19982         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19983         
19984         var nodeIsBefore   =  ss == 1;
19985         var nodeIsAfter    = ee == -1;
19986         
19987         if (nodeIsBefore && nodeIsAfter)
19988             return 0; // outer
19989         if (!nodeIsBefore && nodeIsAfter)
19990             return 1; //right trailed.
19991         
19992         if (nodeIsBefore && !nodeIsAfter)
19993             return 2;  // left trailed.
19994         // fully contined.
19995         return 3;
19996     },
19997
19998     // private? - in a new class?
19999     cleanUpPaste :  function()
20000     {
20001         // cleans up the whole document..
20002         Roo.log('cleanuppaste');
20003         
20004         this.cleanUpChildren(this.doc.body);
20005         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20006         if (clean != this.doc.body.innerHTML) {
20007             this.doc.body.innerHTML = clean;
20008         }
20009         
20010     },
20011     
20012     cleanWordChars : function(input) {// change the chars to hex code
20013         var he = Roo.HtmlEditorCore;
20014         
20015         var output = input;
20016         Roo.each(he.swapCodes, function(sw) { 
20017             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20018             
20019             output = output.replace(swapper, sw[1]);
20020         });
20021         
20022         return output;
20023     },
20024     
20025     
20026     cleanUpChildren : function (n)
20027     {
20028         if (!n.childNodes.length) {
20029             return;
20030         }
20031         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20032            this.cleanUpChild(n.childNodes[i]);
20033         }
20034     },
20035     
20036     
20037         
20038     
20039     cleanUpChild : function (node)
20040     {
20041         var ed = this;
20042         //console.log(node);
20043         if (node.nodeName == "#text") {
20044             // clean up silly Windows -- stuff?
20045             return; 
20046         }
20047         if (node.nodeName == "#comment") {
20048             node.parentNode.removeChild(node);
20049             // clean up silly Windows -- stuff?
20050             return; 
20051         }
20052         var lcname = node.tagName.toLowerCase();
20053         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20054         // whitelist of tags..
20055         
20056         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20057             // remove node.
20058             node.parentNode.removeChild(node);
20059             return;
20060             
20061         }
20062         
20063         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20064         
20065         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20066         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20067         
20068         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20069         //    remove_keep_children = true;
20070         //}
20071         
20072         if (remove_keep_children) {
20073             this.cleanUpChildren(node);
20074             // inserts everything just before this node...
20075             while (node.childNodes.length) {
20076                 var cn = node.childNodes[0];
20077                 node.removeChild(cn);
20078                 node.parentNode.insertBefore(cn, node);
20079             }
20080             node.parentNode.removeChild(node);
20081             return;
20082         }
20083         
20084         if (!node.attributes || !node.attributes.length) {
20085             this.cleanUpChildren(node);
20086             return;
20087         }
20088         
20089         function cleanAttr(n,v)
20090         {
20091             
20092             if (v.match(/^\./) || v.match(/^\//)) {
20093                 return;
20094             }
20095             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20096                 return;
20097             }
20098             if (v.match(/^#/)) {
20099                 return;
20100             }
20101 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20102             node.removeAttribute(n);
20103             
20104         }
20105         
20106         var cwhite = this.cwhite;
20107         var cblack = this.cblack;
20108             
20109         function cleanStyle(n,v)
20110         {
20111             if (v.match(/expression/)) { //XSS?? should we even bother..
20112                 node.removeAttribute(n);
20113                 return;
20114             }
20115             
20116             var parts = v.split(/;/);
20117             var clean = [];
20118             
20119             Roo.each(parts, function(p) {
20120                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20121                 if (!p.length) {
20122                     return true;
20123                 }
20124                 var l = p.split(':').shift().replace(/\s+/g,'');
20125                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20126                 
20127                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20128 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20129                     //node.removeAttribute(n);
20130                     return true;
20131                 }
20132                 //Roo.log()
20133                 // only allow 'c whitelisted system attributes'
20134                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20135 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20136                     //node.removeAttribute(n);
20137                     return true;
20138                 }
20139                 
20140                 
20141                  
20142                 
20143                 clean.push(p);
20144                 return true;
20145             });
20146             if (clean.length) { 
20147                 node.setAttribute(n, clean.join(';'));
20148             } else {
20149                 node.removeAttribute(n);
20150             }
20151             
20152         }
20153         
20154         
20155         for (var i = node.attributes.length-1; i > -1 ; i--) {
20156             var a = node.attributes[i];
20157             //console.log(a);
20158             
20159             if (a.name.toLowerCase().substr(0,2)=='on')  {
20160                 node.removeAttribute(a.name);
20161                 continue;
20162             }
20163             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20164                 node.removeAttribute(a.name);
20165                 continue;
20166             }
20167             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20168                 cleanAttr(a.name,a.value); // fixme..
20169                 continue;
20170             }
20171             if (a.name == 'style') {
20172                 cleanStyle(a.name,a.value);
20173                 continue;
20174             }
20175             /// clean up MS crap..
20176             // tecnically this should be a list of valid class'es..
20177             
20178             
20179             if (a.name == 'class') {
20180                 if (a.value.match(/^Mso/)) {
20181                     node.className = '';
20182                 }
20183                 
20184                 if (a.value.match(/body/)) {
20185                     node.className = '';
20186                 }
20187                 continue;
20188             }
20189             
20190             // style cleanup!?
20191             // class cleanup?
20192             
20193         }
20194         
20195         
20196         this.cleanUpChildren(node);
20197         
20198         
20199     },
20200     
20201     /**
20202      * Clean up MS wordisms...
20203      */
20204     cleanWord : function(node)
20205     {
20206         
20207         
20208         if (!node) {
20209             this.cleanWord(this.doc.body);
20210             return;
20211         }
20212         if (node.nodeName == "#text") {
20213             // clean up silly Windows -- stuff?
20214             return; 
20215         }
20216         if (node.nodeName == "#comment") {
20217             node.parentNode.removeChild(node);
20218             // clean up silly Windows -- stuff?
20219             return; 
20220         }
20221         
20222         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20223             node.parentNode.removeChild(node);
20224             return;
20225         }
20226         
20227         // remove - but keep children..
20228         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20229             while (node.childNodes.length) {
20230                 var cn = node.childNodes[0];
20231                 node.removeChild(cn);
20232                 node.parentNode.insertBefore(cn, node);
20233             }
20234             node.parentNode.removeChild(node);
20235             this.iterateChildren(node, this.cleanWord);
20236             return;
20237         }
20238         // clean styles
20239         if (node.className.length) {
20240             
20241             var cn = node.className.split(/\W+/);
20242             var cna = [];
20243             Roo.each(cn, function(cls) {
20244                 if (cls.match(/Mso[a-zA-Z]+/)) {
20245                     return;
20246                 }
20247                 cna.push(cls);
20248             });
20249             node.className = cna.length ? cna.join(' ') : '';
20250             if (!cna.length) {
20251                 node.removeAttribute("class");
20252             }
20253         }
20254         
20255         if (node.hasAttribute("lang")) {
20256             node.removeAttribute("lang");
20257         }
20258         
20259         if (node.hasAttribute("style")) {
20260             
20261             var styles = node.getAttribute("style").split(";");
20262             var nstyle = [];
20263             Roo.each(styles, function(s) {
20264                 if (!s.match(/:/)) {
20265                     return;
20266                 }
20267                 var kv = s.split(":");
20268                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20269                     return;
20270                 }
20271                 // what ever is left... we allow.
20272                 nstyle.push(s);
20273             });
20274             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20275             if (!nstyle.length) {
20276                 node.removeAttribute('style');
20277             }
20278         }
20279         this.iterateChildren(node, this.cleanWord);
20280         
20281         
20282         
20283     },
20284     /**
20285      * iterateChildren of a Node, calling fn each time, using this as the scole..
20286      * @param {DomNode} node node to iterate children of.
20287      * @param {Function} fn method of this class to call on each item.
20288      */
20289     iterateChildren : function(node, fn)
20290     {
20291         if (!node.childNodes.length) {
20292                 return;
20293         }
20294         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20295            fn.call(this, node.childNodes[i])
20296         }
20297     },
20298     
20299     
20300     /**
20301      * cleanTableWidths.
20302      *
20303      * Quite often pasting from word etc.. results in tables with column and widths.
20304      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20305      *
20306      */
20307     cleanTableWidths : function(node)
20308     {
20309          
20310          
20311         if (!node) {
20312             this.cleanTableWidths(this.doc.body);
20313             return;
20314         }
20315         
20316         // ignore list...
20317         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20318             return; 
20319         }
20320         Roo.log(node.tagName);
20321         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20322             this.iterateChildren(node, this.cleanTableWidths);
20323             return;
20324         }
20325         if (node.hasAttribute('width')) {
20326             node.removeAttribute('width');
20327         }
20328         
20329          
20330         if (node.hasAttribute("style")) {
20331             // pretty basic...
20332             
20333             var styles = node.getAttribute("style").split(";");
20334             var nstyle = [];
20335             Roo.each(styles, function(s) {
20336                 if (!s.match(/:/)) {
20337                     return;
20338                 }
20339                 var kv = s.split(":");
20340                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20341                     return;
20342                 }
20343                 // what ever is left... we allow.
20344                 nstyle.push(s);
20345             });
20346             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20347             if (!nstyle.length) {
20348                 node.removeAttribute('style');
20349             }
20350         }
20351         
20352         this.iterateChildren(node, this.cleanTableWidths);
20353         
20354         
20355     },
20356     
20357     
20358     
20359     
20360     domToHTML : function(currentElement, depth, nopadtext) {
20361         
20362         depth = depth || 0;
20363         nopadtext = nopadtext || false;
20364     
20365         if (!currentElement) {
20366             return this.domToHTML(this.doc.body);
20367         }
20368         
20369         //Roo.log(currentElement);
20370         var j;
20371         var allText = false;
20372         var nodeName = currentElement.nodeName;
20373         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20374         
20375         if  (nodeName == '#text') {
20376             
20377             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20378         }
20379         
20380         
20381         var ret = '';
20382         if (nodeName != 'BODY') {
20383              
20384             var i = 0;
20385             // Prints the node tagName, such as <A>, <IMG>, etc
20386             if (tagName) {
20387                 var attr = [];
20388                 for(i = 0; i < currentElement.attributes.length;i++) {
20389                     // quoting?
20390                     var aname = currentElement.attributes.item(i).name;
20391                     if (!currentElement.attributes.item(i).value.length) {
20392                         continue;
20393                     }
20394                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20395                 }
20396                 
20397                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20398             } 
20399             else {
20400                 
20401                 // eack
20402             }
20403         } else {
20404             tagName = false;
20405         }
20406         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20407             return ret;
20408         }
20409         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20410             nopadtext = true;
20411         }
20412         
20413         
20414         // Traverse the tree
20415         i = 0;
20416         var currentElementChild = currentElement.childNodes.item(i);
20417         var allText = true;
20418         var innerHTML  = '';
20419         lastnode = '';
20420         while (currentElementChild) {
20421             // Formatting code (indent the tree so it looks nice on the screen)
20422             var nopad = nopadtext;
20423             if (lastnode == 'SPAN') {
20424                 nopad  = true;
20425             }
20426             // text
20427             if  (currentElementChild.nodeName == '#text') {
20428                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20429                 toadd = nopadtext ? toadd : toadd.trim();
20430                 if (!nopad && toadd.length > 80) {
20431                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20432                 }
20433                 innerHTML  += toadd;
20434                 
20435                 i++;
20436                 currentElementChild = currentElement.childNodes.item(i);
20437                 lastNode = '';
20438                 continue;
20439             }
20440             allText = false;
20441             
20442             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20443                 
20444             // Recursively traverse the tree structure of the child node
20445             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20446             lastnode = currentElementChild.nodeName;
20447             i++;
20448             currentElementChild=currentElement.childNodes.item(i);
20449         }
20450         
20451         ret += innerHTML;
20452         
20453         if (!allText) {
20454                 // The remaining code is mostly for formatting the tree
20455             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20456         }
20457         
20458         
20459         if (tagName) {
20460             ret+= "</"+tagName+">";
20461         }
20462         return ret;
20463         
20464     },
20465         
20466     applyBlacklists : function()
20467     {
20468         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20469         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20470         
20471         this.white = [];
20472         this.black = [];
20473         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20474             if (b.indexOf(tag) > -1) {
20475                 return;
20476             }
20477             this.white.push(tag);
20478             
20479         }, this);
20480         
20481         Roo.each(w, function(tag) {
20482             if (b.indexOf(tag) > -1) {
20483                 return;
20484             }
20485             if (this.white.indexOf(tag) > -1) {
20486                 return;
20487             }
20488             this.white.push(tag);
20489             
20490         }, this);
20491         
20492         
20493         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20494             if (w.indexOf(tag) > -1) {
20495                 return;
20496             }
20497             this.black.push(tag);
20498             
20499         }, this);
20500         
20501         Roo.each(b, function(tag) {
20502             if (w.indexOf(tag) > -1) {
20503                 return;
20504             }
20505             if (this.black.indexOf(tag) > -1) {
20506                 return;
20507             }
20508             this.black.push(tag);
20509             
20510         }, this);
20511         
20512         
20513         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20514         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20515         
20516         this.cwhite = [];
20517         this.cblack = [];
20518         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20519             if (b.indexOf(tag) > -1) {
20520                 return;
20521             }
20522             this.cwhite.push(tag);
20523             
20524         }, this);
20525         
20526         Roo.each(w, function(tag) {
20527             if (b.indexOf(tag) > -1) {
20528                 return;
20529             }
20530             if (this.cwhite.indexOf(tag) > -1) {
20531                 return;
20532             }
20533             this.cwhite.push(tag);
20534             
20535         }, this);
20536         
20537         
20538         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20539             if (w.indexOf(tag) > -1) {
20540                 return;
20541             }
20542             this.cblack.push(tag);
20543             
20544         }, this);
20545         
20546         Roo.each(b, function(tag) {
20547             if (w.indexOf(tag) > -1) {
20548                 return;
20549             }
20550             if (this.cblack.indexOf(tag) > -1) {
20551                 return;
20552             }
20553             this.cblack.push(tag);
20554             
20555         }, this);
20556     },
20557     
20558     setStylesheets : function(stylesheets)
20559     {
20560         if(typeof(stylesheets) == 'string'){
20561             Roo.get(this.iframe.contentDocument.head).createChild({
20562                 tag : 'link',
20563                 rel : 'stylesheet',
20564                 type : 'text/css',
20565                 href : stylesheets
20566             });
20567             
20568             return;
20569         }
20570         var _this = this;
20571      
20572         Roo.each(stylesheets, function(s) {
20573             if(!s.length){
20574                 return;
20575             }
20576             
20577             Roo.get(_this.iframe.contentDocument.head).createChild({
20578                 tag : 'link',
20579                 rel : 'stylesheet',
20580                 type : 'text/css',
20581                 href : s
20582             });
20583         });
20584
20585         
20586     },
20587     
20588     removeStylesheets : function()
20589     {
20590         var _this = this;
20591         
20592         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20593             s.remove();
20594         });
20595     }
20596     
20597     // hide stuff that is not compatible
20598     /**
20599      * @event blur
20600      * @hide
20601      */
20602     /**
20603      * @event change
20604      * @hide
20605      */
20606     /**
20607      * @event focus
20608      * @hide
20609      */
20610     /**
20611      * @event specialkey
20612      * @hide
20613      */
20614     /**
20615      * @cfg {String} fieldClass @hide
20616      */
20617     /**
20618      * @cfg {String} focusClass @hide
20619      */
20620     /**
20621      * @cfg {String} autoCreate @hide
20622      */
20623     /**
20624      * @cfg {String} inputType @hide
20625      */
20626     /**
20627      * @cfg {String} invalidClass @hide
20628      */
20629     /**
20630      * @cfg {String} invalidText @hide
20631      */
20632     /**
20633      * @cfg {String} msgFx @hide
20634      */
20635     /**
20636      * @cfg {String} validateOnBlur @hide
20637      */
20638 });
20639
20640 Roo.HtmlEditorCore.white = [
20641         'area', 'br', 'img', 'input', 'hr', 'wbr',
20642         
20643        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20644        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20645        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20646        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20647        'table',   'ul',         'xmp', 
20648        
20649        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20650       'thead',   'tr', 
20651      
20652       'dir', 'menu', 'ol', 'ul', 'dl',
20653        
20654       'embed',  'object'
20655 ];
20656
20657
20658 Roo.HtmlEditorCore.black = [
20659     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20660         'applet', // 
20661         'base',   'basefont', 'bgsound', 'blink',  'body', 
20662         'frame',  'frameset', 'head',    'html',   'ilayer', 
20663         'iframe', 'layer',  'link',     'meta',    'object',   
20664         'script', 'style' ,'title',  'xml' // clean later..
20665 ];
20666 Roo.HtmlEditorCore.clean = [
20667     'script', 'style', 'title', 'xml'
20668 ];
20669 Roo.HtmlEditorCore.remove = [
20670     'font'
20671 ];
20672 // attributes..
20673
20674 Roo.HtmlEditorCore.ablack = [
20675     'on'
20676 ];
20677     
20678 Roo.HtmlEditorCore.aclean = [ 
20679     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20680 ];
20681
20682 // protocols..
20683 Roo.HtmlEditorCore.pwhite= [
20684         'http',  'https',  'mailto'
20685 ];
20686
20687 // white listed style attributes.
20688 Roo.HtmlEditorCore.cwhite= [
20689       //  'text-align', /// default is to allow most things..
20690       
20691          
20692 //        'font-size'//??
20693 ];
20694
20695 // black listed style attributes.
20696 Roo.HtmlEditorCore.cblack= [
20697       //  'font-size' -- this can be set by the project 
20698 ];
20699
20700
20701 Roo.HtmlEditorCore.swapCodes   =[ 
20702     [    8211, "--" ], 
20703     [    8212, "--" ], 
20704     [    8216,  "'" ],  
20705     [    8217, "'" ],  
20706     [    8220, '"' ],  
20707     [    8221, '"' ],  
20708     [    8226, "*" ],  
20709     [    8230, "..." ]
20710 ]; 
20711
20712     /*
20713  * - LGPL
20714  *
20715  * HtmlEditor
20716  * 
20717  */
20718
20719 /**
20720  * @class Roo.bootstrap.HtmlEditor
20721  * @extends Roo.bootstrap.TextArea
20722  * Bootstrap HtmlEditor class
20723
20724  * @constructor
20725  * Create a new HtmlEditor
20726  * @param {Object} config The config object
20727  */
20728
20729 Roo.bootstrap.HtmlEditor = function(config){
20730     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20731     if (!this.toolbars) {
20732         this.toolbars = [];
20733     }
20734     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20735     this.addEvents({
20736             /**
20737              * @event initialize
20738              * Fires when the editor is fully initialized (including the iframe)
20739              * @param {HtmlEditor} this
20740              */
20741             initialize: true,
20742             /**
20743              * @event activate
20744              * Fires when the editor is first receives the focus. Any insertion must wait
20745              * until after this event.
20746              * @param {HtmlEditor} this
20747              */
20748             activate: true,
20749              /**
20750              * @event beforesync
20751              * Fires before the textarea is updated with content from the editor iframe. Return false
20752              * to cancel the sync.
20753              * @param {HtmlEditor} this
20754              * @param {String} html
20755              */
20756             beforesync: true,
20757              /**
20758              * @event beforepush
20759              * Fires before the iframe editor is updated with content from the textarea. Return false
20760              * to cancel the push.
20761              * @param {HtmlEditor} this
20762              * @param {String} html
20763              */
20764             beforepush: true,
20765              /**
20766              * @event sync
20767              * Fires when the textarea is updated with content from the editor iframe.
20768              * @param {HtmlEditor} this
20769              * @param {String} html
20770              */
20771             sync: true,
20772              /**
20773              * @event push
20774              * Fires when the iframe editor is updated with content from the textarea.
20775              * @param {HtmlEditor} this
20776              * @param {String} html
20777              */
20778             push: true,
20779              /**
20780              * @event editmodechange
20781              * Fires when the editor switches edit modes
20782              * @param {HtmlEditor} this
20783              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20784              */
20785             editmodechange: true,
20786             /**
20787              * @event editorevent
20788              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20789              * @param {HtmlEditor} this
20790              */
20791             editorevent: true,
20792             /**
20793              * @event firstfocus
20794              * Fires when on first focus - needed by toolbars..
20795              * @param {HtmlEditor} this
20796              */
20797             firstfocus: true,
20798             /**
20799              * @event autosave
20800              * Auto save the htmlEditor value as a file into Events
20801              * @param {HtmlEditor} this
20802              */
20803             autosave: true,
20804             /**
20805              * @event savedpreview
20806              * preview the saved version of htmlEditor
20807              * @param {HtmlEditor} this
20808              */
20809             savedpreview: true
20810         });
20811 };
20812
20813
20814 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20815     
20816     
20817       /**
20818      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20819      */
20820     toolbars : false,
20821    
20822      /**
20823      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20824      *                        Roo.resizable.
20825      */
20826     resizable : false,
20827      /**
20828      * @cfg {Number} height (in pixels)
20829      */   
20830     height: 300,
20831    /**
20832      * @cfg {Number} width (in pixels)
20833      */   
20834     width: false,
20835     
20836     /**
20837      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20838      * 
20839      */
20840     stylesheets: false,
20841     
20842     // id of frame..
20843     frameId: false,
20844     
20845     // private properties
20846     validationEvent : false,
20847     deferHeight: true,
20848     initialized : false,
20849     activated : false,
20850     
20851     onFocus : Roo.emptyFn,
20852     iframePad:3,
20853     hideMode:'offsets',
20854     
20855     
20856     tbContainer : false,
20857     
20858     toolbarContainer :function() {
20859         return this.wrap.select('.x-html-editor-tb',true).first();
20860     },
20861
20862     /**
20863      * Protected method that will not generally be called directly. It
20864      * is called when the editor creates its toolbar. Override this method if you need to
20865      * add custom toolbar buttons.
20866      * @param {HtmlEditor} editor
20867      */
20868     createToolbar : function(){
20869         
20870         Roo.log("create toolbars");
20871         
20872         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20873         this.toolbars[0].render(this.toolbarContainer());
20874         
20875         return;
20876         
20877 //        if (!editor.toolbars || !editor.toolbars.length) {
20878 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20879 //        }
20880 //        
20881 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20882 //            editor.toolbars[i] = Roo.factory(
20883 //                    typeof(editor.toolbars[i]) == 'string' ?
20884 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20885 //                Roo.bootstrap.HtmlEditor);
20886 //            editor.toolbars[i].init(editor);
20887 //        }
20888     },
20889
20890      
20891     // private
20892     onRender : function(ct, position)
20893     {
20894        // Roo.log("Call onRender: " + this.xtype);
20895         var _t = this;
20896         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20897       
20898         this.wrap = this.inputEl().wrap({
20899             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20900         });
20901         
20902         this.editorcore.onRender(ct, position);
20903          
20904         if (this.resizable) {
20905             this.resizeEl = new Roo.Resizable(this.wrap, {
20906                 pinned : true,
20907                 wrap: true,
20908                 dynamic : true,
20909                 minHeight : this.height,
20910                 height: this.height,
20911                 handles : this.resizable,
20912                 width: this.width,
20913                 listeners : {
20914                     resize : function(r, w, h) {
20915                         _t.onResize(w,h); // -something
20916                     }
20917                 }
20918             });
20919             
20920         }
20921         this.createToolbar(this);
20922        
20923         
20924         if(!this.width && this.resizable){
20925             this.setSize(this.wrap.getSize());
20926         }
20927         if (this.resizeEl) {
20928             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20929             // should trigger onReize..
20930         }
20931         
20932     },
20933
20934     // private
20935     onResize : function(w, h)
20936     {
20937         Roo.log('resize: ' +w + ',' + h );
20938         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20939         var ew = false;
20940         var eh = false;
20941         
20942         if(this.inputEl() ){
20943             if(typeof w == 'number'){
20944                 var aw = w - this.wrap.getFrameWidth('lr');
20945                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20946                 ew = aw;
20947             }
20948             if(typeof h == 'number'){
20949                  var tbh = -11;  // fixme it needs to tool bar size!
20950                 for (var i =0; i < this.toolbars.length;i++) {
20951                     // fixme - ask toolbars for heights?
20952                     tbh += this.toolbars[i].el.getHeight();
20953                     //if (this.toolbars[i].footer) {
20954                     //    tbh += this.toolbars[i].footer.el.getHeight();
20955                     //}
20956                 }
20957               
20958                 
20959                 
20960                 
20961                 
20962                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20963                 ah -= 5; // knock a few pixes off for look..
20964                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20965                 var eh = ah;
20966             }
20967         }
20968         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20969         this.editorcore.onResize(ew,eh);
20970         
20971     },
20972
20973     /**
20974      * Toggles the editor between standard and source edit mode.
20975      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20976      */
20977     toggleSourceEdit : function(sourceEditMode)
20978     {
20979         this.editorcore.toggleSourceEdit(sourceEditMode);
20980         
20981         if(this.editorcore.sourceEditMode){
20982             Roo.log('editor - showing textarea');
20983             
20984 //            Roo.log('in');
20985 //            Roo.log(this.syncValue());
20986             this.syncValue();
20987             this.inputEl().removeClass(['hide', 'x-hidden']);
20988             this.inputEl().dom.removeAttribute('tabIndex');
20989             this.inputEl().focus();
20990         }else{
20991             Roo.log('editor - hiding textarea');
20992 //            Roo.log('out')
20993 //            Roo.log(this.pushValue()); 
20994             this.pushValue();
20995             
20996             this.inputEl().addClass(['hide', 'x-hidden']);
20997             this.inputEl().dom.setAttribute('tabIndex', -1);
20998             //this.deferFocus();
20999         }
21000          
21001         if(this.resizable){
21002             this.setSize(this.wrap.getSize());
21003         }
21004         
21005         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21006     },
21007  
21008     // private (for BoxComponent)
21009     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21010
21011     // private (for BoxComponent)
21012     getResizeEl : function(){
21013         return this.wrap;
21014     },
21015
21016     // private (for BoxComponent)
21017     getPositionEl : function(){
21018         return this.wrap;
21019     },
21020
21021     // private
21022     initEvents : function(){
21023         this.originalValue = this.getValue();
21024     },
21025
21026 //    /**
21027 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21028 //     * @method
21029 //     */
21030 //    markInvalid : Roo.emptyFn,
21031 //    /**
21032 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21033 //     * @method
21034 //     */
21035 //    clearInvalid : Roo.emptyFn,
21036
21037     setValue : function(v){
21038         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21039         this.editorcore.pushValue();
21040     },
21041
21042      
21043     // private
21044     deferFocus : function(){
21045         this.focus.defer(10, this);
21046     },
21047
21048     // doc'ed in Field
21049     focus : function(){
21050         this.editorcore.focus();
21051         
21052     },
21053       
21054
21055     // private
21056     onDestroy : function(){
21057         
21058         
21059         
21060         if(this.rendered){
21061             
21062             for (var i =0; i < this.toolbars.length;i++) {
21063                 // fixme - ask toolbars for heights?
21064                 this.toolbars[i].onDestroy();
21065             }
21066             
21067             this.wrap.dom.innerHTML = '';
21068             this.wrap.remove();
21069         }
21070     },
21071
21072     // private
21073     onFirstFocus : function(){
21074         //Roo.log("onFirstFocus");
21075         this.editorcore.onFirstFocus();
21076          for (var i =0; i < this.toolbars.length;i++) {
21077             this.toolbars[i].onFirstFocus();
21078         }
21079         
21080     },
21081     
21082     // private
21083     syncValue : function()
21084     {   
21085         this.editorcore.syncValue();
21086     },
21087     
21088     pushValue : function()
21089     {   
21090         this.editorcore.pushValue();
21091     }
21092      
21093     
21094     // hide stuff that is not compatible
21095     /**
21096      * @event blur
21097      * @hide
21098      */
21099     /**
21100      * @event change
21101      * @hide
21102      */
21103     /**
21104      * @event focus
21105      * @hide
21106      */
21107     /**
21108      * @event specialkey
21109      * @hide
21110      */
21111     /**
21112      * @cfg {String} fieldClass @hide
21113      */
21114     /**
21115      * @cfg {String} focusClass @hide
21116      */
21117     /**
21118      * @cfg {String} autoCreate @hide
21119      */
21120     /**
21121      * @cfg {String} inputType @hide
21122      */
21123     /**
21124      * @cfg {String} invalidClass @hide
21125      */
21126     /**
21127      * @cfg {String} invalidText @hide
21128      */
21129     /**
21130      * @cfg {String} msgFx @hide
21131      */
21132     /**
21133      * @cfg {String} validateOnBlur @hide
21134      */
21135 });
21136  
21137     
21138    
21139    
21140    
21141       
21142 Roo.namespace('Roo.bootstrap.htmleditor');
21143 /**
21144  * @class Roo.bootstrap.HtmlEditorToolbar1
21145  * Basic Toolbar
21146  * 
21147  * Usage:
21148  *
21149  new Roo.bootstrap.HtmlEditor({
21150     ....
21151     toolbars : [
21152         new Roo.bootstrap.HtmlEditorToolbar1({
21153             disable : { fonts: 1 , format: 1, ..., ... , ...],
21154             btns : [ .... ]
21155         })
21156     }
21157      
21158  * 
21159  * @cfg {Object} disable List of elements to disable..
21160  * @cfg {Array} btns List of additional buttons.
21161  * 
21162  * 
21163  * NEEDS Extra CSS? 
21164  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21165  */
21166  
21167 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21168 {
21169     
21170     Roo.apply(this, config);
21171     
21172     // default disabled, based on 'good practice'..
21173     this.disable = this.disable || {};
21174     Roo.applyIf(this.disable, {
21175         fontSize : true,
21176         colors : true,
21177         specialElements : true
21178     });
21179     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21180     
21181     this.editor = config.editor;
21182     this.editorcore = config.editor.editorcore;
21183     
21184     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21185     
21186     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21187     // dont call parent... till later.
21188 }
21189 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21190      
21191     bar : true,
21192     
21193     editor : false,
21194     editorcore : false,
21195     
21196     
21197     formats : [
21198         "p" ,  
21199         "h1","h2","h3","h4","h5","h6", 
21200         "pre", "code", 
21201         "abbr", "acronym", "address", "cite", "samp", "var",
21202         'div','span'
21203     ],
21204     
21205     onRender : function(ct, position)
21206     {
21207        // Roo.log("Call onRender: " + this.xtype);
21208         
21209        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21210        Roo.log(this.el);
21211        this.el.dom.style.marginBottom = '0';
21212        var _this = this;
21213        var editorcore = this.editorcore;
21214        var editor= this.editor;
21215        
21216        var children = [];
21217        var btn = function(id,cmd , toggle, handler){
21218        
21219             var  event = toggle ? 'toggle' : 'click';
21220        
21221             var a = {
21222                 size : 'sm',
21223                 xtype: 'Button',
21224                 xns: Roo.bootstrap,
21225                 glyphicon : id,
21226                 cmd : id || cmd,
21227                 enableToggle:toggle !== false,
21228                 //html : 'submit'
21229                 pressed : toggle ? false : null,
21230                 listeners : {}
21231             };
21232             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21233                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21234             };
21235             children.push(a);
21236             return a;
21237        }
21238         
21239         var style = {
21240                 xtype: 'Button',
21241                 size : 'sm',
21242                 xns: Roo.bootstrap,
21243                 glyphicon : 'font',
21244                 //html : 'submit'
21245                 menu : {
21246                     xtype: 'Menu',
21247                     xns: Roo.bootstrap,
21248                     items:  []
21249                 }
21250         };
21251         Roo.each(this.formats, function(f) {
21252             style.menu.items.push({
21253                 xtype :'MenuItem',
21254                 xns: Roo.bootstrap,
21255                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21256                 tagname : f,
21257                 listeners : {
21258                     click : function()
21259                     {
21260                         editorcore.insertTag(this.tagname);
21261                         editor.focus();
21262                     }
21263                 }
21264                 
21265             });
21266         });
21267          children.push(style);   
21268             
21269             
21270         btn('bold',false,true);
21271         btn('italic',false,true);
21272         btn('align-left', 'justifyleft',true);
21273         btn('align-center', 'justifycenter',true);
21274         btn('align-right' , 'justifyright',true);
21275         btn('link', false, false, function(btn) {
21276             //Roo.log("create link?");
21277             var url = prompt(this.createLinkText, this.defaultLinkValue);
21278             if(url && url != 'http:/'+'/'){
21279                 this.editorcore.relayCmd('createlink', url);
21280             }
21281         }),
21282         btn('list','insertunorderedlist',true);
21283         btn('pencil', false,true, function(btn){
21284                 Roo.log(this);
21285                 
21286                 this.toggleSourceEdit(btn.pressed);
21287         });
21288         /*
21289         var cog = {
21290                 xtype: 'Button',
21291                 size : 'sm',
21292                 xns: Roo.bootstrap,
21293                 glyphicon : 'cog',
21294                 //html : 'submit'
21295                 menu : {
21296                     xtype: 'Menu',
21297                     xns: Roo.bootstrap,
21298                     items:  []
21299                 }
21300         };
21301         
21302         cog.menu.items.push({
21303             xtype :'MenuItem',
21304             xns: Roo.bootstrap,
21305             html : Clean styles,
21306             tagname : f,
21307             listeners : {
21308                 click : function()
21309                 {
21310                     editorcore.insertTag(this.tagname);
21311                     editor.focus();
21312                 }
21313             }
21314             
21315         });
21316        */
21317         
21318          
21319        this.xtype = 'NavSimplebar';
21320         
21321         for(var i=0;i< children.length;i++) {
21322             
21323             this.buttons.add(this.addxtypeChild(children[i]));
21324             
21325         }
21326         
21327         editor.on('editorevent', this.updateToolbar, this);
21328     },
21329     onBtnClick : function(id)
21330     {
21331        this.editorcore.relayCmd(id);
21332        this.editorcore.focus();
21333     },
21334     
21335     /**
21336      * Protected method that will not generally be called directly. It triggers
21337      * a toolbar update by reading the markup state of the current selection in the editor.
21338      */
21339     updateToolbar: function(){
21340
21341         if(!this.editorcore.activated){
21342             this.editor.onFirstFocus(); // is this neeed?
21343             return;
21344         }
21345
21346         var btns = this.buttons; 
21347         var doc = this.editorcore.doc;
21348         btns.get('bold').setActive(doc.queryCommandState('bold'));
21349         btns.get('italic').setActive(doc.queryCommandState('italic'));
21350         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21351         
21352         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21353         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21354         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21355         
21356         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21357         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21358          /*
21359         
21360         var ans = this.editorcore.getAllAncestors();
21361         if (this.formatCombo) {
21362             
21363             
21364             var store = this.formatCombo.store;
21365             this.formatCombo.setValue("");
21366             for (var i =0; i < ans.length;i++) {
21367                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21368                     // select it..
21369                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21370                     break;
21371                 }
21372             }
21373         }
21374         
21375         
21376         
21377         // hides menus... - so this cant be on a menu...
21378         Roo.bootstrap.MenuMgr.hideAll();
21379         */
21380         Roo.bootstrap.MenuMgr.hideAll();
21381         //this.editorsyncValue();
21382     },
21383     onFirstFocus: function() {
21384         this.buttons.each(function(item){
21385            item.enable();
21386         });
21387     },
21388     toggleSourceEdit : function(sourceEditMode){
21389         
21390           
21391         if(sourceEditMode){
21392             Roo.log("disabling buttons");
21393            this.buttons.each( function(item){
21394                 if(item.cmd != 'pencil'){
21395                     item.disable();
21396                 }
21397             });
21398           
21399         }else{
21400             Roo.log("enabling buttons");
21401             if(this.editorcore.initialized){
21402                 this.buttons.each( function(item){
21403                     item.enable();
21404                 });
21405             }
21406             
21407         }
21408         Roo.log("calling toggole on editor");
21409         // tell the editor that it's been pressed..
21410         this.editor.toggleSourceEdit(sourceEditMode);
21411        
21412     }
21413 });
21414
21415
21416
21417
21418
21419 /**
21420  * @class Roo.bootstrap.Table.AbstractSelectionModel
21421  * @extends Roo.util.Observable
21422  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21423  * implemented by descendant classes.  This class should not be directly instantiated.
21424  * @constructor
21425  */
21426 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21427     this.locked = false;
21428     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21429 };
21430
21431
21432 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21433     /** @ignore Called by the grid automatically. Do not call directly. */
21434     init : function(grid){
21435         this.grid = grid;
21436         this.initEvents();
21437     },
21438
21439     /**
21440      * Locks the selections.
21441      */
21442     lock : function(){
21443         this.locked = true;
21444     },
21445
21446     /**
21447      * Unlocks the selections.
21448      */
21449     unlock : function(){
21450         this.locked = false;
21451     },
21452
21453     /**
21454      * Returns true if the selections are locked.
21455      * @return {Boolean}
21456      */
21457     isLocked : function(){
21458         return this.locked;
21459     }
21460 });
21461 /**
21462  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21463  * @class Roo.bootstrap.Table.RowSelectionModel
21464  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21465  * It supports multiple selections and keyboard selection/navigation. 
21466  * @constructor
21467  * @param {Object} config
21468  */
21469
21470 Roo.bootstrap.Table.RowSelectionModel = function(config){
21471     Roo.apply(this, config);
21472     this.selections = new Roo.util.MixedCollection(false, function(o){
21473         return o.id;
21474     });
21475
21476     this.last = false;
21477     this.lastActive = false;
21478
21479     this.addEvents({
21480         /**
21481              * @event selectionchange
21482              * Fires when the selection changes
21483              * @param {SelectionModel} this
21484              */
21485             "selectionchange" : true,
21486         /**
21487              * @event afterselectionchange
21488              * Fires after the selection changes (eg. by key press or clicking)
21489              * @param {SelectionModel} this
21490              */
21491             "afterselectionchange" : true,
21492         /**
21493              * @event beforerowselect
21494              * Fires when a row is selected being selected, return false to cancel.
21495              * @param {SelectionModel} this
21496              * @param {Number} rowIndex The selected index
21497              * @param {Boolean} keepExisting False if other selections will be cleared
21498              */
21499             "beforerowselect" : true,
21500         /**
21501              * @event rowselect
21502              * Fires when a row is selected.
21503              * @param {SelectionModel} this
21504              * @param {Number} rowIndex The selected index
21505              * @param {Roo.data.Record} r The record
21506              */
21507             "rowselect" : true,
21508         /**
21509              * @event rowdeselect
21510              * Fires when a row is deselected.
21511              * @param {SelectionModel} this
21512              * @param {Number} rowIndex The selected index
21513              */
21514         "rowdeselect" : true
21515     });
21516     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21517     this.locked = false;
21518 };
21519
21520 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21521     /**
21522      * @cfg {Boolean} singleSelect
21523      * True to allow selection of only one row at a time (defaults to false)
21524      */
21525     singleSelect : false,
21526
21527     // private
21528     initEvents : function(){
21529
21530         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21531             this.grid.on("mousedown", this.handleMouseDown, this);
21532         }else{ // allow click to work like normal
21533             this.grid.on("rowclick", this.handleDragableRowClick, this);
21534         }
21535
21536         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21537             "up" : function(e){
21538                 if(!e.shiftKey){
21539                     this.selectPrevious(e.shiftKey);
21540                 }else if(this.last !== false && this.lastActive !== false){
21541                     var last = this.last;
21542                     this.selectRange(this.last,  this.lastActive-1);
21543                     this.grid.getView().focusRow(this.lastActive);
21544                     if(last !== false){
21545                         this.last = last;
21546                     }
21547                 }else{
21548                     this.selectFirstRow();
21549                 }
21550                 this.fireEvent("afterselectionchange", this);
21551             },
21552             "down" : function(e){
21553                 if(!e.shiftKey){
21554                     this.selectNext(e.shiftKey);
21555                 }else if(this.last !== false && this.lastActive !== false){
21556                     var last = this.last;
21557                     this.selectRange(this.last,  this.lastActive+1);
21558                     this.grid.getView().focusRow(this.lastActive);
21559                     if(last !== false){
21560                         this.last = last;
21561                     }
21562                 }else{
21563                     this.selectFirstRow();
21564                 }
21565                 this.fireEvent("afterselectionchange", this);
21566             },
21567             scope: this
21568         });
21569
21570         var view = this.grid.view;
21571         view.on("refresh", this.onRefresh, this);
21572         view.on("rowupdated", this.onRowUpdated, this);
21573         view.on("rowremoved", this.onRemove, this);
21574     },
21575
21576     // private
21577     onRefresh : function(){
21578         var ds = this.grid.dataSource, i, v = this.grid.view;
21579         var s = this.selections;
21580         s.each(function(r){
21581             if((i = ds.indexOfId(r.id)) != -1){
21582                 v.onRowSelect(i);
21583             }else{
21584                 s.remove(r);
21585             }
21586         });
21587     },
21588
21589     // private
21590     onRemove : function(v, index, r){
21591         this.selections.remove(r);
21592     },
21593
21594     // private
21595     onRowUpdated : function(v, index, r){
21596         if(this.isSelected(r)){
21597             v.onRowSelect(index);
21598         }
21599     },
21600
21601     /**
21602      * Select records.
21603      * @param {Array} records The records to select
21604      * @param {Boolean} keepExisting (optional) True to keep existing selections
21605      */
21606     selectRecords : function(records, keepExisting){
21607         if(!keepExisting){
21608             this.clearSelections();
21609         }
21610         var ds = this.grid.dataSource;
21611         for(var i = 0, len = records.length; i < len; i++){
21612             this.selectRow(ds.indexOf(records[i]), true);
21613         }
21614     },
21615
21616     /**
21617      * Gets the number of selected rows.
21618      * @return {Number}
21619      */
21620     getCount : function(){
21621         return this.selections.length;
21622     },
21623
21624     /**
21625      * Selects the first row in the grid.
21626      */
21627     selectFirstRow : function(){
21628         this.selectRow(0);
21629     },
21630
21631     /**
21632      * Select the last row.
21633      * @param {Boolean} keepExisting (optional) True to keep existing selections
21634      */
21635     selectLastRow : function(keepExisting){
21636         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21637     },
21638
21639     /**
21640      * Selects the row immediately following the last selected row.
21641      * @param {Boolean} keepExisting (optional) True to keep existing selections
21642      */
21643     selectNext : function(keepExisting){
21644         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21645             this.selectRow(this.last+1, keepExisting);
21646             this.grid.getView().focusRow(this.last);
21647         }
21648     },
21649
21650     /**
21651      * Selects the row that precedes the last selected row.
21652      * @param {Boolean} keepExisting (optional) True to keep existing selections
21653      */
21654     selectPrevious : function(keepExisting){
21655         if(this.last){
21656             this.selectRow(this.last-1, keepExisting);
21657             this.grid.getView().focusRow(this.last);
21658         }
21659     },
21660
21661     /**
21662      * Returns the selected records
21663      * @return {Array} Array of selected records
21664      */
21665     getSelections : function(){
21666         return [].concat(this.selections.items);
21667     },
21668
21669     /**
21670      * Returns the first selected record.
21671      * @return {Record}
21672      */
21673     getSelected : function(){
21674         return this.selections.itemAt(0);
21675     },
21676
21677
21678     /**
21679      * Clears all selections.
21680      */
21681     clearSelections : function(fast){
21682         if(this.locked) return;
21683         if(fast !== true){
21684             var ds = this.grid.dataSource;
21685             var s = this.selections;
21686             s.each(function(r){
21687                 this.deselectRow(ds.indexOfId(r.id));
21688             }, this);
21689             s.clear();
21690         }else{
21691             this.selections.clear();
21692         }
21693         this.last = false;
21694     },
21695
21696
21697     /**
21698      * Selects all rows.
21699      */
21700     selectAll : function(){
21701         if(this.locked) return;
21702         this.selections.clear();
21703         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21704             this.selectRow(i, true);
21705         }
21706     },
21707
21708     /**
21709      * Returns True if there is a selection.
21710      * @return {Boolean}
21711      */
21712     hasSelection : function(){
21713         return this.selections.length > 0;
21714     },
21715
21716     /**
21717      * Returns True if the specified row is selected.
21718      * @param {Number/Record} record The record or index of the record to check
21719      * @return {Boolean}
21720      */
21721     isSelected : function(index){
21722         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21723         return (r && this.selections.key(r.id) ? true : false);
21724     },
21725
21726     /**
21727      * Returns True if the specified record id is selected.
21728      * @param {String} id The id of record to check
21729      * @return {Boolean}
21730      */
21731     isIdSelected : function(id){
21732         return (this.selections.key(id) ? true : false);
21733     },
21734
21735     // private
21736     handleMouseDown : function(e, t){
21737         var view = this.grid.getView(), rowIndex;
21738         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21739             return;
21740         };
21741         if(e.shiftKey && this.last !== false){
21742             var last = this.last;
21743             this.selectRange(last, rowIndex, e.ctrlKey);
21744             this.last = last; // reset the last
21745             view.focusRow(rowIndex);
21746         }else{
21747             var isSelected = this.isSelected(rowIndex);
21748             if(e.button !== 0 && isSelected){
21749                 view.focusRow(rowIndex);
21750             }else if(e.ctrlKey && isSelected){
21751                 this.deselectRow(rowIndex);
21752             }else if(!isSelected){
21753                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21754                 view.focusRow(rowIndex);
21755             }
21756         }
21757         this.fireEvent("afterselectionchange", this);
21758     },
21759     // private
21760     handleDragableRowClick :  function(grid, rowIndex, e) 
21761     {
21762         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21763             this.selectRow(rowIndex, false);
21764             grid.view.focusRow(rowIndex);
21765              this.fireEvent("afterselectionchange", this);
21766         }
21767     },
21768     
21769     /**
21770      * Selects multiple rows.
21771      * @param {Array} rows Array of the indexes of the row to select
21772      * @param {Boolean} keepExisting (optional) True to keep existing selections
21773      */
21774     selectRows : function(rows, keepExisting){
21775         if(!keepExisting){
21776             this.clearSelections();
21777         }
21778         for(var i = 0, len = rows.length; i < len; i++){
21779             this.selectRow(rows[i], true);
21780         }
21781     },
21782
21783     /**
21784      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21785      * @param {Number} startRow The index of the first row in the range
21786      * @param {Number} endRow The index of the last row in the range
21787      * @param {Boolean} keepExisting (optional) True to retain existing selections
21788      */
21789     selectRange : function(startRow, endRow, keepExisting){
21790         if(this.locked) return;
21791         if(!keepExisting){
21792             this.clearSelections();
21793         }
21794         if(startRow <= endRow){
21795             for(var i = startRow; i <= endRow; i++){
21796                 this.selectRow(i, true);
21797             }
21798         }else{
21799             for(var i = startRow; i >= endRow; i--){
21800                 this.selectRow(i, true);
21801             }
21802         }
21803     },
21804
21805     /**
21806      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21807      * @param {Number} startRow The index of the first row in the range
21808      * @param {Number} endRow The index of the last row in the range
21809      */
21810     deselectRange : function(startRow, endRow, preventViewNotify){
21811         if(this.locked) return;
21812         for(var i = startRow; i <= endRow; i++){
21813             this.deselectRow(i, preventViewNotify);
21814         }
21815     },
21816
21817     /**
21818      * Selects a row.
21819      * @param {Number} row The index of the row to select
21820      * @param {Boolean} keepExisting (optional) True to keep existing selections
21821      */
21822     selectRow : function(index, keepExisting, preventViewNotify){
21823         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21824         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21825             if(!keepExisting || this.singleSelect){
21826                 this.clearSelections();
21827             }
21828             var r = this.grid.dataSource.getAt(index);
21829             this.selections.add(r);
21830             this.last = this.lastActive = index;
21831             if(!preventViewNotify){
21832                 this.grid.getView().onRowSelect(index);
21833             }
21834             this.fireEvent("rowselect", this, index, r);
21835             this.fireEvent("selectionchange", this);
21836         }
21837     },
21838
21839     /**
21840      * Deselects a row.
21841      * @param {Number} row The index of the row to deselect
21842      */
21843     deselectRow : function(index, preventViewNotify){
21844         if(this.locked) return;
21845         if(this.last == index){
21846             this.last = false;
21847         }
21848         if(this.lastActive == index){
21849             this.lastActive = false;
21850         }
21851         var r = this.grid.dataSource.getAt(index);
21852         this.selections.remove(r);
21853         if(!preventViewNotify){
21854             this.grid.getView().onRowDeselect(index);
21855         }
21856         this.fireEvent("rowdeselect", this, index);
21857         this.fireEvent("selectionchange", this);
21858     },
21859
21860     // private
21861     restoreLast : function(){
21862         if(this._last){
21863             this.last = this._last;
21864         }
21865     },
21866
21867     // private
21868     acceptsNav : function(row, col, cm){
21869         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21870     },
21871
21872     // private
21873     onEditorKey : function(field, e){
21874         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21875         if(k == e.TAB){
21876             e.stopEvent();
21877             ed.completeEdit();
21878             if(e.shiftKey){
21879                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21880             }else{
21881                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21882             }
21883         }else if(k == e.ENTER && !e.ctrlKey){
21884             e.stopEvent();
21885             ed.completeEdit();
21886             if(e.shiftKey){
21887                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21888             }else{
21889                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21890             }
21891         }else if(k == e.ESC){
21892             ed.cancelEdit();
21893         }
21894         if(newCell){
21895             g.startEditing(newCell[0], newCell[1]);
21896         }
21897     }
21898 });/*
21899  * Based on:
21900  * Ext JS Library 1.1.1
21901  * Copyright(c) 2006-2007, Ext JS, LLC.
21902  *
21903  * Originally Released Under LGPL - original licence link has changed is not relivant.
21904  *
21905  * Fork - LGPL
21906  * <script type="text/javascript">
21907  */
21908  
21909 /**
21910  * @class Roo.bootstrap.PagingToolbar
21911  * @extends Roo.bootstrap.NavSimplebar
21912  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21913  * @constructor
21914  * Create a new PagingToolbar
21915  * @param {Object} config The config object
21916  * @param {Roo.data.Store} store
21917  */
21918 Roo.bootstrap.PagingToolbar = function(config)
21919 {
21920     // old args format still supported... - xtype is prefered..
21921         // created from xtype...
21922     
21923     this.ds = config.dataSource;
21924     
21925     if (config.store && !this.ds) {
21926         this.store= Roo.factory(config.store, Roo.data);
21927         this.ds = this.store;
21928         this.ds.xmodule = this.xmodule || false;
21929     }
21930     
21931     this.toolbarItems = [];
21932     if (config.items) {
21933         this.toolbarItems = config.items;
21934     }
21935     
21936     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21937     
21938     this.cursor = 0;
21939     
21940     if (this.ds) { 
21941         this.bind(this.ds);
21942     }
21943     
21944     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21945     
21946 };
21947
21948 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21949     /**
21950      * @cfg {Roo.data.Store} dataSource
21951      * The underlying data store providing the paged data
21952      */
21953     /**
21954      * @cfg {String/HTMLElement/Element} container
21955      * container The id or element that will contain the toolbar
21956      */
21957     /**
21958      * @cfg {Boolean} displayInfo
21959      * True to display the displayMsg (defaults to false)
21960      */
21961     /**
21962      * @cfg {Number} pageSize
21963      * The number of records to display per page (defaults to 20)
21964      */
21965     pageSize: 20,
21966     /**
21967      * @cfg {String} displayMsg
21968      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21969      */
21970     displayMsg : 'Displaying {0} - {1} of {2}',
21971     /**
21972      * @cfg {String} emptyMsg
21973      * The message to display when no records are found (defaults to "No data to display")
21974      */
21975     emptyMsg : 'No data to display',
21976     /**
21977      * Customizable piece of the default paging text (defaults to "Page")
21978      * @type String
21979      */
21980     beforePageText : "Page",
21981     /**
21982      * Customizable piece of the default paging text (defaults to "of %0")
21983      * @type String
21984      */
21985     afterPageText : "of {0}",
21986     /**
21987      * Customizable piece of the default paging text (defaults to "First Page")
21988      * @type String
21989      */
21990     firstText : "First Page",
21991     /**
21992      * Customizable piece of the default paging text (defaults to "Previous Page")
21993      * @type String
21994      */
21995     prevText : "Previous Page",
21996     /**
21997      * Customizable piece of the default paging text (defaults to "Next Page")
21998      * @type String
21999      */
22000     nextText : "Next Page",
22001     /**
22002      * Customizable piece of the default paging text (defaults to "Last Page")
22003      * @type String
22004      */
22005     lastText : "Last Page",
22006     /**
22007      * Customizable piece of the default paging text (defaults to "Refresh")
22008      * @type String
22009      */
22010     refreshText : "Refresh",
22011
22012     buttons : false,
22013     // private
22014     onRender : function(ct, position) 
22015     {
22016         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22017         this.navgroup.parentId = this.id;
22018         this.navgroup.onRender(this.el, null);
22019         // add the buttons to the navgroup
22020         
22021         if(this.displayInfo){
22022             Roo.log(this.el.select('ul.navbar-nav',true).first());
22023             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22024             this.displayEl = this.el.select('.x-paging-info', true).first();
22025 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22026 //            this.displayEl = navel.el.select('span',true).first();
22027         }
22028         
22029         var _this = this;
22030         
22031         if(this.buttons){
22032             Roo.each(_this.buttons, function(e){ // this might need to use render????
22033                Roo.factory(e).onRender(_this.el, null);
22034             });
22035         }
22036             
22037         Roo.each(_this.toolbarItems, function(e) {
22038             _this.navgroup.addItem(e);
22039         });
22040         
22041         
22042         this.first = this.navgroup.addItem({
22043             tooltip: this.firstText,
22044             cls: "prev",
22045             icon : 'fa fa-backward',
22046             disabled: true,
22047             preventDefault: true,
22048             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22049         });
22050         
22051         this.prev =  this.navgroup.addItem({
22052             tooltip: this.prevText,
22053             cls: "prev",
22054             icon : 'fa fa-step-backward',
22055             disabled: true,
22056             preventDefault: true,
22057             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22058         });
22059     //this.addSeparator();
22060         
22061         
22062         var field = this.navgroup.addItem( {
22063             tagtype : 'span',
22064             cls : 'x-paging-position',
22065             
22066             html : this.beforePageText  +
22067                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22068                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22069          } ); //?? escaped?
22070         
22071         this.field = field.el.select('input', true).first();
22072         this.field.on("keydown", this.onPagingKeydown, this);
22073         this.field.on("focus", function(){this.dom.select();});
22074     
22075     
22076         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22077         //this.field.setHeight(18);
22078         //this.addSeparator();
22079         this.next = this.navgroup.addItem({
22080             tooltip: this.nextText,
22081             cls: "next",
22082             html : ' <i class="fa fa-step-forward">',
22083             disabled: true,
22084             preventDefault: true,
22085             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22086         });
22087         this.last = this.navgroup.addItem({
22088             tooltip: this.lastText,
22089             icon : 'fa fa-forward',
22090             cls: "next",
22091             disabled: true,
22092             preventDefault: true,
22093             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22094         });
22095     //this.addSeparator();
22096         this.loading = this.navgroup.addItem({
22097             tooltip: this.refreshText,
22098             icon: 'fa fa-refresh',
22099             preventDefault: true,
22100             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22101         });
22102         
22103     },
22104
22105     // private
22106     updateInfo : function(){
22107         if(this.displayEl){
22108             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22109             var msg = count == 0 ?
22110                 this.emptyMsg :
22111                 String.format(
22112                     this.displayMsg,
22113                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22114                 );
22115             this.displayEl.update(msg);
22116         }
22117     },
22118
22119     // private
22120     onLoad : function(ds, r, o){
22121        this.cursor = o.params ? o.params.start : 0;
22122        var d = this.getPageData(),
22123             ap = d.activePage,
22124             ps = d.pages;
22125         
22126        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22127        this.field.dom.value = ap;
22128        this.first.setDisabled(ap == 1);
22129        this.prev.setDisabled(ap == 1);
22130        this.next.setDisabled(ap == ps);
22131        this.last.setDisabled(ap == ps);
22132        this.loading.enable();
22133        this.updateInfo();
22134     },
22135
22136     // private
22137     getPageData : function(){
22138         var total = this.ds.getTotalCount();
22139         return {
22140             total : total,
22141             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22142             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22143         };
22144     },
22145
22146     // private
22147     onLoadError : function(){
22148         this.loading.enable();
22149     },
22150
22151     // private
22152     onPagingKeydown : function(e){
22153         var k = e.getKey();
22154         var d = this.getPageData();
22155         if(k == e.RETURN){
22156             var v = this.field.dom.value, pageNum;
22157             if(!v || isNaN(pageNum = parseInt(v, 10))){
22158                 this.field.dom.value = d.activePage;
22159                 return;
22160             }
22161             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22162             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22163             e.stopEvent();
22164         }
22165         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
22166         {
22167           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22168           this.field.dom.value = pageNum;
22169           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22170           e.stopEvent();
22171         }
22172         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22173         {
22174           var v = this.field.dom.value, pageNum; 
22175           var increment = (e.shiftKey) ? 10 : 1;
22176           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22177             increment *= -1;
22178           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22179             this.field.dom.value = d.activePage;
22180             return;
22181           }
22182           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22183           {
22184             this.field.dom.value = parseInt(v, 10) + increment;
22185             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22186             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22187           }
22188           e.stopEvent();
22189         }
22190     },
22191
22192     // private
22193     beforeLoad : function(){
22194         if(this.loading){
22195             this.loading.disable();
22196         }
22197     },
22198
22199     // private
22200     onClick : function(which){
22201         
22202         var ds = this.ds;
22203         if (!ds) {
22204             return;
22205         }
22206         
22207         switch(which){
22208             case "first":
22209                 ds.load({params:{start: 0, limit: this.pageSize}});
22210             break;
22211             case "prev":
22212                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22213             break;
22214             case "next":
22215                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22216             break;
22217             case "last":
22218                 var total = ds.getTotalCount();
22219                 var extra = total % this.pageSize;
22220                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22221                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22222             break;
22223             case "refresh":
22224                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22225             break;
22226         }
22227     },
22228
22229     /**
22230      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22231      * @param {Roo.data.Store} store The data store to unbind
22232      */
22233     unbind : function(ds){
22234         ds.un("beforeload", this.beforeLoad, this);
22235         ds.un("load", this.onLoad, this);
22236         ds.un("loadexception", this.onLoadError, this);
22237         ds.un("remove", this.updateInfo, this);
22238         ds.un("add", this.updateInfo, this);
22239         this.ds = undefined;
22240     },
22241
22242     /**
22243      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22244      * @param {Roo.data.Store} store The data store to bind
22245      */
22246     bind : function(ds){
22247         ds.on("beforeload", this.beforeLoad, this);
22248         ds.on("load", this.onLoad, this);
22249         ds.on("loadexception", this.onLoadError, this);
22250         ds.on("remove", this.updateInfo, this);
22251         ds.on("add", this.updateInfo, this);
22252         this.ds = ds;
22253     }
22254 });/*
22255  * - LGPL
22256  *
22257  * element
22258  * 
22259  */
22260
22261 /**
22262  * @class Roo.bootstrap.MessageBar
22263  * @extends Roo.bootstrap.Component
22264  * Bootstrap MessageBar class
22265  * @cfg {String} html contents of the MessageBar
22266  * @cfg {String} weight (info | success | warning | danger) default info
22267  * @cfg {String} beforeClass insert the bar before the given class
22268  * @cfg {Boolean} closable (true | false) default false
22269  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22270  * 
22271  * @constructor
22272  * Create a new Element
22273  * @param {Object} config The config object
22274  */
22275
22276 Roo.bootstrap.MessageBar = function(config){
22277     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22278 };
22279
22280 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22281     
22282     html: '',
22283     weight: 'info',
22284     closable: false,
22285     fixed: false,
22286     beforeClass: 'bootstrap-sticky-wrap',
22287     
22288     getAutoCreate : function(){
22289         
22290         var cfg = {
22291             tag: 'div',
22292             cls: 'alert alert-dismissable alert-' + this.weight,
22293             cn: [
22294                 {
22295                     tag: 'span',
22296                     cls: 'message',
22297                     html: this.html || ''
22298                 }
22299             ]
22300         };
22301         
22302         if(this.fixed){
22303             cfg.cls += ' alert-messages-fixed';
22304         }
22305         
22306         if(this.closable){
22307             cfg.cn.push({
22308                 tag: 'button',
22309                 cls: 'close',
22310                 html: 'x'
22311             });
22312         }
22313         
22314         return cfg;
22315     },
22316     
22317     onRender : function(ct, position)
22318     {
22319         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22320         
22321         if(!this.el){
22322             var cfg = Roo.apply({},  this.getAutoCreate());
22323             cfg.id = Roo.id();
22324             
22325             if (this.cls) {
22326                 cfg.cls += ' ' + this.cls;
22327             }
22328             if (this.style) {
22329                 cfg.style = this.style;
22330             }
22331             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22332             
22333             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22334         }
22335         
22336         this.el.select('>button.close').on('click', this.hide, this);
22337         
22338     },
22339     
22340     show : function()
22341     {
22342         if (!this.rendered) {
22343             this.render();
22344         }
22345         
22346         this.el.show();
22347         
22348         this.fireEvent('show', this);
22349         
22350     },
22351     
22352     hide : function()
22353     {
22354         if (!this.rendered) {
22355             this.render();
22356         }
22357         
22358         this.el.hide();
22359         
22360         this.fireEvent('hide', this);
22361     },
22362     
22363     update : function()
22364     {
22365 //        var e = this.el.dom.firstChild;
22366 //        
22367 //        if(this.closable){
22368 //            e = e.nextSibling;
22369 //        }
22370 //        
22371 //        e.data = this.html || '';
22372
22373         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22374     }
22375    
22376 });
22377
22378  
22379
22380      /*
22381  * - LGPL
22382  *
22383  * Graph
22384  * 
22385  */
22386
22387
22388 /**
22389  * @class Roo.bootstrap.Graph
22390  * @extends Roo.bootstrap.Component
22391  * Bootstrap Graph class
22392 > Prameters
22393  -sm {number} sm 4
22394  -md {number} md 5
22395  @cfg {String} graphtype  bar | vbar | pie
22396  @cfg {number} g_x coodinator | centre x (pie)
22397  @cfg {number} g_y coodinator | centre y (pie)
22398  @cfg {number} g_r radius (pie)
22399  @cfg {number} g_height height of the chart (respected by all elements in the set)
22400  @cfg {number} g_width width of the chart (respected by all elements in the set)
22401  @cfg {Object} title The title of the chart
22402     
22403  -{Array}  values
22404  -opts (object) options for the chart 
22405      o {
22406      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22407      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22408      o vgutter (number)
22409      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
22410      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22411      o to
22412      o stretch (boolean)
22413      o }
22414  -opts (object) options for the pie
22415      o{
22416      o cut
22417      o startAngle (number)
22418      o endAngle (number)
22419      } 
22420  *
22421  * @constructor
22422  * Create a new Input
22423  * @param {Object} config The config object
22424  */
22425
22426 Roo.bootstrap.Graph = function(config){
22427     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22428     
22429     this.addEvents({
22430         // img events
22431         /**
22432          * @event click
22433          * The img click event for the img.
22434          * @param {Roo.EventObject} e
22435          */
22436         "click" : true
22437     });
22438 };
22439
22440 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22441     
22442     sm: 4,
22443     md: 5,
22444     graphtype: 'bar',
22445     g_height: 250,
22446     g_width: 400,
22447     g_x: 50,
22448     g_y: 50,
22449     g_r: 30,
22450     opts:{
22451         //g_colors: this.colors,
22452         g_type: 'soft',
22453         g_gutter: '20%'
22454
22455     },
22456     title : false,
22457
22458     getAutoCreate : function(){
22459         
22460         var cfg = {
22461             tag: 'div',
22462             html : null
22463         };
22464         
22465         
22466         return  cfg;
22467     },
22468
22469     onRender : function(ct,position){
22470         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22471         this.raphael = Raphael(this.el.dom);
22472         
22473                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22474                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22475                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22476                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22477                 /*
22478                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22479                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22480                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22481                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22482                 
22483                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22484                 r.barchart(330, 10, 300, 220, data1);
22485                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22486                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22487                 */
22488                 
22489                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22490                 // r.barchart(30, 30, 560, 250,  xdata, {
22491                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22492                 //     axis : "0 0 1 1",
22493                 //     axisxlabels :  xdata
22494                 //     //yvalues : cols,
22495                    
22496                 // });
22497 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22498 //        
22499 //        this.load(null,xdata,{
22500 //                axis : "0 0 1 1",
22501 //                axisxlabels :  xdata
22502 //                });
22503
22504     },
22505
22506     load : function(graphtype,xdata,opts){
22507         this.raphael.clear();
22508         if(!graphtype) {
22509             graphtype = this.graphtype;
22510         }
22511         if(!opts){
22512             opts = this.opts;
22513         }
22514         var r = this.raphael,
22515             fin = function () {
22516                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22517             },
22518             fout = function () {
22519                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22520             },
22521             pfin = function() {
22522                 this.sector.stop();
22523                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22524
22525                 if (this.label) {
22526                     this.label[0].stop();
22527                     this.label[0].attr({ r: 7.5 });
22528                     this.label[1].attr({ "font-weight": 800 });
22529                 }
22530             },
22531             pfout = function() {
22532                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22533
22534                 if (this.label) {
22535                     this.label[0].animate({ r: 5 }, 500, "bounce");
22536                     this.label[1].attr({ "font-weight": 400 });
22537                 }
22538             };
22539
22540         switch(graphtype){
22541             case 'bar':
22542                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22543                 break;
22544             case 'hbar':
22545                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22546                 break;
22547             case 'pie':
22548 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22549 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22550 //            
22551                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22552                 
22553                 break;
22554
22555         }
22556         
22557         if(this.title){
22558             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22559         }
22560         
22561     },
22562     
22563     setTitle: function(o)
22564     {
22565         this.title = o;
22566     },
22567     
22568     initEvents: function() {
22569         
22570         if(!this.href){
22571             this.el.on('click', this.onClick, this);
22572         }
22573     },
22574     
22575     onClick : function(e)
22576     {
22577         Roo.log('img onclick');
22578         this.fireEvent('click', this, e);
22579     }
22580    
22581 });
22582
22583  
22584 /*
22585  * - LGPL
22586  *
22587  * numberBox
22588  * 
22589  */
22590 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22591
22592 /**
22593  * @class Roo.bootstrap.dash.NumberBox
22594  * @extends Roo.bootstrap.Component
22595  * Bootstrap NumberBox class
22596  * @cfg {String} headline Box headline
22597  * @cfg {String} content Box content
22598  * @cfg {String} icon Box icon
22599  * @cfg {String} footer Footer text
22600  * @cfg {String} fhref Footer href
22601  * 
22602  * @constructor
22603  * Create a new NumberBox
22604  * @param {Object} config The config object
22605  */
22606
22607
22608 Roo.bootstrap.dash.NumberBox = function(config){
22609     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22610     
22611 };
22612
22613 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22614     
22615     headline : '',
22616     content : '',
22617     icon : '',
22618     footer : '',
22619     fhref : '',
22620     ficon : '',
22621     
22622     getAutoCreate : function(){
22623         
22624         var cfg = {
22625             tag : 'div',
22626             cls : 'small-box ',
22627             cn : [
22628                 {
22629                     tag : 'div',
22630                     cls : 'inner',
22631                     cn :[
22632                         {
22633                             tag : 'h3',
22634                             cls : 'roo-headline',
22635                             html : this.headline
22636                         },
22637                         {
22638                             tag : 'p',
22639                             cls : 'roo-content',
22640                             html : this.content
22641                         }
22642                     ]
22643                 }
22644             ]
22645         };
22646         
22647         if(this.icon){
22648             cfg.cn.push({
22649                 tag : 'div',
22650                 cls : 'icon',
22651                 cn :[
22652                     {
22653                         tag : 'i',
22654                         cls : 'ion ' + this.icon
22655                     }
22656                 ]
22657             });
22658         }
22659         
22660         if(this.footer){
22661             var footer = {
22662                 tag : 'a',
22663                 cls : 'small-box-footer',
22664                 href : this.fhref || '#',
22665                 html : this.footer
22666             };
22667             
22668             cfg.cn.push(footer);
22669             
22670         }
22671         
22672         return  cfg;
22673     },
22674
22675     onRender : function(ct,position){
22676         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22677
22678
22679        
22680                 
22681     },
22682
22683     setHeadline: function (value)
22684     {
22685         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22686     },
22687     
22688     setFooter: function (value, href)
22689     {
22690         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22691         
22692         if(href){
22693             this.el.select('a.small-box-footer',true).first().attr('href', href);
22694         }
22695         
22696     },
22697
22698     setContent: function (value)
22699     {
22700         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22701     },
22702
22703     initEvents: function() 
22704     {   
22705         
22706     }
22707     
22708 });
22709
22710  
22711 /*
22712  * - LGPL
22713  *
22714  * TabBox
22715  * 
22716  */
22717 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22718
22719 /**
22720  * @class Roo.bootstrap.dash.TabBox
22721  * @extends Roo.bootstrap.Component
22722  * Bootstrap TabBox class
22723  * @cfg {String} title Title of the TabBox
22724  * @cfg {String} icon Icon of the TabBox
22725  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22726  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22727  * 
22728  * @constructor
22729  * Create a new TabBox
22730  * @param {Object} config The config object
22731  */
22732
22733
22734 Roo.bootstrap.dash.TabBox = function(config){
22735     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22736     this.addEvents({
22737         // raw events
22738         /**
22739          * @event addpane
22740          * When a pane is added
22741          * @param {Roo.bootstrap.dash.TabPane} pane
22742          */
22743         "addpane" : true,
22744         /**
22745          * @event activatepane
22746          * When a pane is activated
22747          * @param {Roo.bootstrap.dash.TabPane} pane
22748          */
22749         "activatepane" : true
22750         
22751          
22752     });
22753     
22754     this.panes = [];
22755 };
22756
22757 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22758
22759     title : '',
22760     icon : false,
22761     showtabs : true,
22762     tabScrollable : false,
22763     
22764     getChildContainer : function()
22765     {
22766         return this.el.select('.tab-content', true).first();
22767     },
22768     
22769     getAutoCreate : function(){
22770         
22771         var header = {
22772             tag: 'li',
22773             cls: 'pull-left header',
22774             html: this.title,
22775             cn : []
22776         };
22777         
22778         if(this.icon){
22779             header.cn.push({
22780                 tag: 'i',
22781                 cls: 'fa ' + this.icon
22782             });
22783         }
22784         
22785         var h = {
22786             tag: 'ul',
22787             cls: 'nav nav-tabs pull-right',
22788             cn: [
22789                 header
22790             ]
22791         };
22792         
22793         if(this.tabScrollable){
22794             h = {
22795                 tag: 'div',
22796                 cls: 'tab-header',
22797                 cn: [
22798                     {
22799                         tag: 'ul',
22800                         cls: 'nav nav-tabs pull-right',
22801                         cn: [
22802                             header
22803                         ]
22804                     }
22805                 ]
22806             };
22807         }
22808         
22809         var cfg = {
22810             tag: 'div',
22811             cls: 'nav-tabs-custom',
22812             cn: [
22813                 h,
22814                 {
22815                     tag: 'div',
22816                     cls: 'tab-content no-padding',
22817                     cn: []
22818                 }
22819             ]
22820         };
22821
22822         return  cfg;
22823     },
22824     initEvents : function()
22825     {
22826         //Roo.log('add add pane handler');
22827         this.on('addpane', this.onAddPane, this);
22828     },
22829      /**
22830      * Updates the box title
22831      * @param {String} html to set the title to.
22832      */
22833     setTitle : function(value)
22834     {
22835         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22836     },
22837     onAddPane : function(pane)
22838     {
22839         this.panes.push(pane);
22840         //Roo.log('addpane');
22841         //Roo.log(pane);
22842         // tabs are rendere left to right..
22843         if(!this.showtabs){
22844             return;
22845         }
22846         
22847         var ctr = this.el.select('.nav-tabs', true).first();
22848          
22849          
22850         var existing = ctr.select('.nav-tab',true);
22851         var qty = existing.getCount();;
22852         
22853         
22854         var tab = ctr.createChild({
22855             tag : 'li',
22856             cls : 'nav-tab' + (qty ? '' : ' active'),
22857             cn : [
22858                 {
22859                     tag : 'a',
22860                     href:'#',
22861                     html : pane.title
22862                 }
22863             ]
22864         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22865         pane.tab = tab;
22866         
22867         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22868         if (!qty) {
22869             pane.el.addClass('active');
22870         }
22871         
22872                 
22873     },
22874     onTabClick : function(ev,un,ob,pane)
22875     {
22876         //Roo.log('tab - prev default');
22877         ev.preventDefault();
22878         
22879         
22880         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22881         pane.tab.addClass('active');
22882         //Roo.log(pane.title);
22883         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22884         // technically we should have a deactivate event.. but maybe add later.
22885         // and it should not de-activate the selected tab...
22886         this.fireEvent('activatepane', pane);
22887         pane.el.addClass('active');
22888         pane.fireEvent('activate');
22889         
22890         
22891     },
22892     
22893     getActivePane : function()
22894     {
22895         var r = false;
22896         Roo.each(this.panes, function(p) {
22897             if(p.el.hasClass('active')){
22898                 r = p;
22899                 return false;
22900             }
22901             
22902             return;
22903         });
22904         
22905         return r;
22906     }
22907     
22908     
22909 });
22910
22911  
22912 /*
22913  * - LGPL
22914  *
22915  * Tab pane
22916  * 
22917  */
22918 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22919 /**
22920  * @class Roo.bootstrap.TabPane
22921  * @extends Roo.bootstrap.Component
22922  * Bootstrap TabPane class
22923  * @cfg {Boolean} active (false | true) Default false
22924  * @cfg {String} title title of panel
22925
22926  * 
22927  * @constructor
22928  * Create a new TabPane
22929  * @param {Object} config The config object
22930  */
22931
22932 Roo.bootstrap.dash.TabPane = function(config){
22933     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22934     
22935     this.addEvents({
22936         // raw events
22937         /**
22938          * @event activate
22939          * When a pane is activated
22940          * @param {Roo.bootstrap.dash.TabPane} pane
22941          */
22942         "activate" : true
22943          
22944     });
22945 };
22946
22947 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22948     
22949     active : false,
22950     title : '',
22951     
22952     // the tabBox that this is attached to.
22953     tab : false,
22954      
22955     getAutoCreate : function() 
22956     {
22957         var cfg = {
22958             tag: 'div',
22959             cls: 'tab-pane'
22960         };
22961         
22962         if(this.active){
22963             cfg.cls += ' active';
22964         }
22965         
22966         return cfg;
22967     },
22968     initEvents  : function()
22969     {
22970         //Roo.log('trigger add pane handler');
22971         this.parent().fireEvent('addpane', this)
22972     },
22973     
22974      /**
22975      * Updates the tab title 
22976      * @param {String} html to set the title to.
22977      */
22978     setTitle: function(str)
22979     {
22980         if (!this.tab) {
22981             return;
22982         }
22983         this.title = str;
22984         this.tab.select('a', true).first().dom.innerHTML = str;
22985         
22986     }
22987     
22988     
22989     
22990 });
22991
22992  
22993
22994
22995  /*
22996  * - LGPL
22997  *
22998  * menu
22999  * 
23000  */
23001 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23002
23003 /**
23004  * @class Roo.bootstrap.menu.Menu
23005  * @extends Roo.bootstrap.Component
23006  * Bootstrap Menu class - container for Menu
23007  * @cfg {String} html Text of the menu
23008  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23009  * @cfg {String} icon Font awesome icon
23010  * @cfg {String} pos Menu align to (top | bottom) default bottom
23011  * 
23012  * 
23013  * @constructor
23014  * Create a new Menu
23015  * @param {Object} config The config object
23016  */
23017
23018
23019 Roo.bootstrap.menu.Menu = function(config){
23020     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23021     
23022     this.addEvents({
23023         /**
23024          * @event beforeshow
23025          * Fires before this menu is displayed
23026          * @param {Roo.bootstrap.menu.Menu} this
23027          */
23028         beforeshow : true,
23029         /**
23030          * @event beforehide
23031          * Fires before this menu is hidden
23032          * @param {Roo.bootstrap.menu.Menu} this
23033          */
23034         beforehide : true,
23035         /**
23036          * @event show
23037          * Fires after this menu is displayed
23038          * @param {Roo.bootstrap.menu.Menu} this
23039          */
23040         show : true,
23041         /**
23042          * @event hide
23043          * Fires after this menu is hidden
23044          * @param {Roo.bootstrap.menu.Menu} this
23045          */
23046         hide : true,
23047         /**
23048          * @event click
23049          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23050          * @param {Roo.bootstrap.menu.Menu} this
23051          * @param {Roo.EventObject} e
23052          */
23053         click : true
23054     });
23055     
23056 };
23057
23058 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23059     
23060     submenu : false,
23061     html : '',
23062     weight : 'default',
23063     icon : false,
23064     pos : 'bottom',
23065     
23066     
23067     getChildContainer : function() {
23068         if(this.isSubMenu){
23069             return this.el;
23070         }
23071         
23072         return this.el.select('ul.dropdown-menu', true).first();  
23073     },
23074     
23075     getAutoCreate : function()
23076     {
23077         var text = [
23078             {
23079                 tag : 'span',
23080                 cls : 'roo-menu-text',
23081                 html : this.html
23082             }
23083         ];
23084         
23085         if(this.icon){
23086             text.unshift({
23087                 tag : 'i',
23088                 cls : 'fa ' + this.icon
23089             })
23090         }
23091         
23092         
23093         var cfg = {
23094             tag : 'div',
23095             cls : 'btn-group',
23096             cn : [
23097                 {
23098                     tag : 'button',
23099                     cls : 'dropdown-button btn btn-' + this.weight,
23100                     cn : text
23101                 },
23102                 {
23103                     tag : 'button',
23104                     cls : 'dropdown-toggle btn btn-' + this.weight,
23105                     cn : [
23106                         {
23107                             tag : 'span',
23108                             cls : 'caret'
23109                         }
23110                     ]
23111                 },
23112                 {
23113                     tag : 'ul',
23114                     cls : 'dropdown-menu'
23115                 }
23116             ]
23117             
23118         };
23119         
23120         if(this.pos == 'top'){
23121             cfg.cls += ' dropup';
23122         }
23123         
23124         if(this.isSubMenu){
23125             cfg = {
23126                 tag : 'ul',
23127                 cls : 'dropdown-menu'
23128             }
23129         }
23130         
23131         return cfg;
23132     },
23133     
23134     onRender : function(ct, position)
23135     {
23136         this.isSubMenu = ct.hasClass('dropdown-submenu');
23137         
23138         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23139     },
23140     
23141     initEvents : function() 
23142     {
23143         if(this.isSubMenu){
23144             return;
23145         }
23146         
23147         this.hidden = true;
23148         
23149         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23150         this.triggerEl.on('click', this.onTriggerPress, this);
23151         
23152         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23153         this.buttonEl.on('click', this.onClick, this);
23154         
23155     },
23156     
23157     list : function()
23158     {
23159         if(this.isSubMenu){
23160             return this.el;
23161         }
23162         
23163         return this.el.select('ul.dropdown-menu', true).first();
23164     },
23165     
23166     onClick : function(e)
23167     {
23168         this.fireEvent("click", this, e);
23169     },
23170     
23171     onTriggerPress  : function(e)
23172     {   
23173         if (this.isVisible()) {
23174             this.hide();
23175         } else {
23176             this.show();
23177         }
23178     },
23179     
23180     isVisible : function(){
23181         return !this.hidden;
23182     },
23183     
23184     show : function()
23185     {
23186         this.fireEvent("beforeshow", this);
23187         
23188         this.hidden = false;
23189         this.el.addClass('open');
23190         
23191         Roo.get(document).on("mouseup", this.onMouseUp, this);
23192         
23193         this.fireEvent("show", this);
23194         
23195         
23196     },
23197     
23198     hide : function()
23199     {
23200         this.fireEvent("beforehide", this);
23201         
23202         this.hidden = true;
23203         this.el.removeClass('open');
23204         
23205         Roo.get(document).un("mouseup", this.onMouseUp);
23206         
23207         this.fireEvent("hide", this);
23208     },
23209     
23210     onMouseUp : function()
23211     {
23212         this.hide();
23213     }
23214     
23215 });
23216
23217  
23218  /*
23219  * - LGPL
23220  *
23221  * menu item
23222  * 
23223  */
23224 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23225
23226 /**
23227  * @class Roo.bootstrap.menu.Item
23228  * @extends Roo.bootstrap.Component
23229  * Bootstrap MenuItem class
23230  * @cfg {Boolean} submenu (true | false) default false
23231  * @cfg {String} html text of the item
23232  * @cfg {String} href the link
23233  * @cfg {Boolean} disable (true | false) default false
23234  * @cfg {Boolean} preventDefault (true | false) default true
23235  * @cfg {String} icon Font awesome icon
23236  * @cfg {String} pos Submenu align to (left | right) default right 
23237  * 
23238  * 
23239  * @constructor
23240  * Create a new Item
23241  * @param {Object} config The config object
23242  */
23243
23244
23245 Roo.bootstrap.menu.Item = function(config){
23246     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23247     this.addEvents({
23248         /**
23249          * @event mouseover
23250          * Fires when the mouse is hovering over this menu
23251          * @param {Roo.bootstrap.menu.Item} this
23252          * @param {Roo.EventObject} e
23253          */
23254         mouseover : true,
23255         /**
23256          * @event mouseout
23257          * Fires when the mouse exits this menu
23258          * @param {Roo.bootstrap.menu.Item} this
23259          * @param {Roo.EventObject} e
23260          */
23261         mouseout : true,
23262         // raw events
23263         /**
23264          * @event click
23265          * The raw click event for the entire grid.
23266          * @param {Roo.EventObject} e
23267          */
23268         click : true
23269     });
23270 };
23271
23272 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23273     
23274     submenu : false,
23275     href : '',
23276     html : '',
23277     preventDefault: true,
23278     disable : false,
23279     icon : false,
23280     pos : 'right',
23281     
23282     getAutoCreate : function()
23283     {
23284         var text = [
23285             {
23286                 tag : 'span',
23287                 cls : 'roo-menu-item-text',
23288                 html : this.html
23289             }
23290         ];
23291         
23292         if(this.icon){
23293             text.unshift({
23294                 tag : 'i',
23295                 cls : 'fa ' + this.icon
23296             })
23297         }
23298         
23299         var cfg = {
23300             tag : 'li',
23301             cn : [
23302                 {
23303                     tag : 'a',
23304                     href : this.href || '#',
23305                     cn : text
23306                 }
23307             ]
23308         };
23309         
23310         if(this.disable){
23311             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23312         }
23313         
23314         if(this.submenu){
23315             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23316             
23317             if(this.pos == 'left'){
23318                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23319             }
23320         }
23321         
23322         return cfg;
23323     },
23324     
23325     initEvents : function() 
23326     {
23327         this.el.on('mouseover', this.onMouseOver, this);
23328         this.el.on('mouseout', this.onMouseOut, this);
23329         
23330         this.el.select('a', true).first().on('click', this.onClick, this);
23331         
23332     },
23333     
23334     onClick : function(e)
23335     {
23336         if(this.preventDefault){
23337             e.preventDefault();
23338         }
23339         
23340         this.fireEvent("click", this, e);
23341     },
23342     
23343     onMouseOver : function(e)
23344     {
23345         if(this.submenu && this.pos == 'left'){
23346             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23347         }
23348         
23349         this.fireEvent("mouseover", this, e);
23350     },
23351     
23352     onMouseOut : function(e)
23353     {
23354         this.fireEvent("mouseout", this, e);
23355     }
23356 });
23357
23358  
23359
23360  /*
23361  * - LGPL
23362  *
23363  * menu separator
23364  * 
23365  */
23366 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23367
23368 /**
23369  * @class Roo.bootstrap.menu.Separator
23370  * @extends Roo.bootstrap.Component
23371  * Bootstrap Separator class
23372  * 
23373  * @constructor
23374  * Create a new Separator
23375  * @param {Object} config The config object
23376  */
23377
23378
23379 Roo.bootstrap.menu.Separator = function(config){
23380     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23381 };
23382
23383 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23384     
23385     getAutoCreate : function(){
23386         var cfg = {
23387             tag : 'li',
23388             cls: 'divider'
23389         };
23390         
23391         return cfg;
23392     }
23393    
23394 });
23395
23396  
23397
23398  /*
23399  * - LGPL
23400  *
23401  * Tooltip
23402  * 
23403  */
23404
23405 /**
23406  * @class Roo.bootstrap.Tooltip
23407  * Bootstrap Tooltip class
23408  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23409  * to determine which dom element triggers the tooltip.
23410  * 
23411  * It needs to add support for additional attributes like tooltip-position
23412  * 
23413  * @constructor
23414  * Create a new Toolti
23415  * @param {Object} config The config object
23416  */
23417
23418 Roo.bootstrap.Tooltip = function(config){
23419     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23420 };
23421
23422 Roo.apply(Roo.bootstrap.Tooltip, {
23423     /**
23424      * @function init initialize tooltip monitoring.
23425      * @static
23426      */
23427     currentEl : false,
23428     currentTip : false,
23429     currentRegion : false,
23430     
23431     //  init : delay?
23432     
23433     init : function()
23434     {
23435         Roo.get(document).on('mouseover', this.enter ,this);
23436         Roo.get(document).on('mouseout', this.leave, this);
23437          
23438         
23439         this.currentTip = new Roo.bootstrap.Tooltip();
23440     },
23441     
23442     enter : function(ev)
23443     {
23444         var dom = ev.getTarget();
23445         
23446         //Roo.log(['enter',dom]);
23447         var el = Roo.fly(dom);
23448         if (this.currentEl) {
23449             //Roo.log(dom);
23450             //Roo.log(this.currentEl);
23451             //Roo.log(this.currentEl.contains(dom));
23452             if (this.currentEl == el) {
23453                 return;
23454             }
23455             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23456                 return;
23457             }
23458
23459         }
23460         
23461         if (this.currentTip.el) {
23462             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23463         }    
23464         //Roo.log(ev);
23465         var bindEl = el;
23466         
23467         // you can not look for children, as if el is the body.. then everythign is the child..
23468         if (!el.attr('tooltip')) { //
23469             if (!el.select("[tooltip]").elements.length) {
23470                 return;
23471             }
23472             // is the mouse over this child...?
23473             bindEl = el.select("[tooltip]").first();
23474             var xy = ev.getXY();
23475             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23476                 //Roo.log("not in region.");
23477                 return;
23478             }
23479             //Roo.log("child element over..");
23480             
23481         }
23482         this.currentEl = bindEl;
23483         this.currentTip.bind(bindEl);
23484         this.currentRegion = Roo.lib.Region.getRegion(dom);
23485         this.currentTip.enter();
23486         
23487     },
23488     leave : function(ev)
23489     {
23490         var dom = ev.getTarget();
23491         //Roo.log(['leave',dom]);
23492         if (!this.currentEl) {
23493             return;
23494         }
23495         
23496         
23497         if (dom != this.currentEl.dom) {
23498             return;
23499         }
23500         var xy = ev.getXY();
23501         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23502             return;
23503         }
23504         // only activate leave if mouse cursor is outside... bounding box..
23505         
23506         
23507         
23508         
23509         if (this.currentTip) {
23510             this.currentTip.leave();
23511         }
23512         //Roo.log('clear currentEl');
23513         this.currentEl = false;
23514         
23515         
23516     },
23517     alignment : {
23518         'left' : ['r-l', [-2,0], 'right'],
23519         'right' : ['l-r', [2,0], 'left'],
23520         'bottom' : ['t-b', [0,2], 'top'],
23521         'top' : [ 'b-t', [0,-2], 'bottom']
23522     }
23523     
23524 });
23525
23526
23527 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23528     
23529     
23530     bindEl : false,
23531     
23532     delay : null, // can be { show : 300 , hide: 500}
23533     
23534     timeout : null,
23535     
23536     hoverState : null, //???
23537     
23538     placement : 'bottom', 
23539     
23540     getAutoCreate : function(){
23541     
23542         var cfg = {
23543            cls : 'tooltip',
23544            role : 'tooltip',
23545            cn : [
23546                 {
23547                     cls : 'tooltip-arrow'
23548                 },
23549                 {
23550                     cls : 'tooltip-inner'
23551                 }
23552            ]
23553         };
23554         
23555         return cfg;
23556     },
23557     bind : function(el)
23558     {
23559         this.bindEl = el;
23560     },
23561       
23562     
23563     enter : function () {
23564        
23565         if (this.timeout != null) {
23566             clearTimeout(this.timeout);
23567         }
23568         
23569         this.hoverState = 'in';
23570          //Roo.log("enter - show");
23571         if (!this.delay || !this.delay.show) {
23572             this.show();
23573             return;
23574         }
23575         var _t = this;
23576         this.timeout = setTimeout(function () {
23577             if (_t.hoverState == 'in') {
23578                 _t.show();
23579             }
23580         }, this.delay.show);
23581     },
23582     leave : function()
23583     {
23584         clearTimeout(this.timeout);
23585     
23586         this.hoverState = 'out';
23587          if (!this.delay || !this.delay.hide) {
23588             this.hide();
23589             return;
23590         }
23591        
23592         var _t = this;
23593         this.timeout = setTimeout(function () {
23594             //Roo.log("leave - timeout");
23595             
23596             if (_t.hoverState == 'out') {
23597                 _t.hide();
23598                 Roo.bootstrap.Tooltip.currentEl = false;
23599             }
23600         }, delay);
23601     },
23602     
23603     show : function ()
23604     {
23605         if (!this.el) {
23606             this.render(document.body);
23607         }
23608         // set content.
23609         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23610         
23611         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23612         
23613         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23614         
23615         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23616         
23617         var placement = typeof this.placement == 'function' ?
23618             this.placement.call(this, this.el, on_el) :
23619             this.placement;
23620             
23621         var autoToken = /\s?auto?\s?/i;
23622         var autoPlace = autoToken.test(placement);
23623         if (autoPlace) {
23624             placement = placement.replace(autoToken, '') || 'top';
23625         }
23626         
23627         //this.el.detach()
23628         //this.el.setXY([0,0]);
23629         this.el.show();
23630         //this.el.dom.style.display='block';
23631         
23632         //this.el.appendTo(on_el);
23633         
23634         var p = this.getPosition();
23635         var box = this.el.getBox();
23636         
23637         if (autoPlace) {
23638             // fixme..
23639         }
23640         
23641         var align = Roo.bootstrap.Tooltip.alignment[placement];
23642         
23643         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23644         
23645         if(placement == 'top' || placement == 'bottom'){
23646             if(xy[0] < 0){
23647                 placement = 'right';
23648             }
23649             
23650             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23651                 placement = 'left';
23652             }
23653         }
23654         
23655         align = Roo.bootstrap.Tooltip.alignment[placement];
23656         
23657         this.el.alignTo(this.bindEl, align[0],align[1]);
23658         //var arrow = this.el.select('.arrow',true).first();
23659         //arrow.set(align[2], 
23660         
23661         this.el.addClass(placement);
23662         
23663         this.el.addClass('in fade');
23664         
23665         this.hoverState = null;
23666         
23667         if (this.el.hasClass('fade')) {
23668             // fade it?
23669         }
23670         
23671     },
23672     hide : function()
23673     {
23674          
23675         if (!this.el) {
23676             return;
23677         }
23678         //this.el.setXY([0,0]);
23679         this.el.removeClass('in');
23680         //this.el.hide();
23681         
23682     }
23683     
23684 });
23685  
23686
23687  /*
23688  * - LGPL
23689  *
23690  * Location Picker
23691  * 
23692  */
23693
23694 /**
23695  * @class Roo.bootstrap.LocationPicker
23696  * @extends Roo.bootstrap.Component
23697  * Bootstrap LocationPicker class
23698  * @cfg {Number} latitude Position when init default 0
23699  * @cfg {Number} longitude Position when init default 0
23700  * @cfg {Number} zoom default 15
23701  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23702  * @cfg {Boolean} mapTypeControl default false
23703  * @cfg {Boolean} disableDoubleClickZoom default false
23704  * @cfg {Boolean} scrollwheel default true
23705  * @cfg {Boolean} streetViewControl default false
23706  * @cfg {Number} radius default 0
23707  * @cfg {String} locationName
23708  * @cfg {Boolean} draggable default true
23709  * @cfg {Boolean} enableAutocomplete default false
23710  * @cfg {Boolean} enableReverseGeocode default true
23711  * @cfg {String} markerTitle
23712  * 
23713  * @constructor
23714  * Create a new LocationPicker
23715  * @param {Object} config The config object
23716  */
23717
23718
23719 Roo.bootstrap.LocationPicker = function(config){
23720     
23721     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23722     
23723     this.addEvents({
23724         /**
23725          * @event initial
23726          * Fires when the picker initialized.
23727          * @param {Roo.bootstrap.LocationPicker} this
23728          * @param {Google Location} location
23729          */
23730         initial : true,
23731         /**
23732          * @event positionchanged
23733          * Fires when the picker position changed.
23734          * @param {Roo.bootstrap.LocationPicker} this
23735          * @param {Google Location} location
23736          */
23737         positionchanged : true,
23738         /**
23739          * @event resize
23740          * Fires when the map resize.
23741          * @param {Roo.bootstrap.LocationPicker} this
23742          */
23743         resize : true,
23744         /**
23745          * @event show
23746          * Fires when the map show.
23747          * @param {Roo.bootstrap.LocationPicker} this
23748          */
23749         show : true,
23750         /**
23751          * @event hide
23752          * Fires when the map hide.
23753          * @param {Roo.bootstrap.LocationPicker} this
23754          */
23755         hide : true,
23756         /**
23757          * @event mapClick
23758          * Fires when click the map.
23759          * @param {Roo.bootstrap.LocationPicker} this
23760          * @param {Map event} e
23761          */
23762         mapClick : true,
23763         /**
23764          * @event mapRightClick
23765          * Fires when right click the map.
23766          * @param {Roo.bootstrap.LocationPicker} this
23767          * @param {Map event} e
23768          */
23769         mapRightClick : true,
23770         /**
23771          * @event markerClick
23772          * Fires when click the marker.
23773          * @param {Roo.bootstrap.LocationPicker} this
23774          * @param {Map event} e
23775          */
23776         markerClick : true,
23777         /**
23778          * @event markerRightClick
23779          * Fires when right click the marker.
23780          * @param {Roo.bootstrap.LocationPicker} this
23781          * @param {Map event} e
23782          */
23783         markerRightClick : true,
23784         /**
23785          * @event OverlayViewDraw
23786          * Fires when OverlayView Draw
23787          * @param {Roo.bootstrap.LocationPicker} this
23788          */
23789         OverlayViewDraw : true,
23790         /**
23791          * @event OverlayViewOnAdd
23792          * Fires when OverlayView Draw
23793          * @param {Roo.bootstrap.LocationPicker} this
23794          */
23795         OverlayViewOnAdd : true,
23796         /**
23797          * @event OverlayViewOnRemove
23798          * Fires when OverlayView Draw
23799          * @param {Roo.bootstrap.LocationPicker} this
23800          */
23801         OverlayViewOnRemove : true,
23802         /**
23803          * @event OverlayViewShow
23804          * Fires when OverlayView Draw
23805          * @param {Roo.bootstrap.LocationPicker} this
23806          * @param {Pixel} cpx
23807          */
23808         OverlayViewShow : true,
23809         /**
23810          * @event OverlayViewHide
23811          * Fires when OverlayView Draw
23812          * @param {Roo.bootstrap.LocationPicker} this
23813          */
23814         OverlayViewHide : true,
23815         /**
23816          * @event loadexception
23817          * Fires when load google lib failed.
23818          * @param {Roo.bootstrap.LocationPicker} this
23819          */
23820         loadexception : true
23821     });
23822         
23823 };
23824
23825 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23826     
23827     gMapContext: false,
23828     
23829     latitude: 0,
23830     longitude: 0,
23831     zoom: 15,
23832     mapTypeId: false,
23833     mapTypeControl: false,
23834     disableDoubleClickZoom: false,
23835     scrollwheel: true,
23836     streetViewControl: false,
23837     radius: 0,
23838     locationName: '',
23839     draggable: true,
23840     enableAutocomplete: false,
23841     enableReverseGeocode: true,
23842     markerTitle: '',
23843     
23844     getAutoCreate: function()
23845     {
23846
23847         var cfg = {
23848             tag: 'div',
23849             cls: 'roo-location-picker'
23850         };
23851         
23852         return cfg
23853     },
23854     
23855     initEvents: function(ct, position)
23856     {       
23857         if(!this.el.getWidth() || this.isApplied()){
23858             return;
23859         }
23860         
23861         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23862         
23863         this.initial();
23864     },
23865     
23866     initial: function()
23867     {
23868         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23869             this.fireEvent('loadexception', this);
23870             return;
23871         }
23872         
23873         if(!this.mapTypeId){
23874             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23875         }
23876         
23877         this.gMapContext = this.GMapContext();
23878         
23879         this.initOverlayView();
23880         
23881         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23882         
23883         var _this = this;
23884                 
23885         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23886             _this.setPosition(_this.gMapContext.marker.position);
23887         });
23888         
23889         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23890             _this.fireEvent('mapClick', this, event);
23891             
23892         });
23893
23894         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23895             _this.fireEvent('mapRightClick', this, event);
23896             
23897         });
23898         
23899         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23900             _this.fireEvent('markerClick', this, event);
23901             
23902         });
23903
23904         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23905             _this.fireEvent('markerRightClick', this, event);
23906             
23907         });
23908         
23909         this.setPosition(this.gMapContext.location);
23910         
23911         this.fireEvent('initial', this, this.gMapContext.location);
23912     },
23913     
23914     initOverlayView: function()
23915     {
23916         var _this = this;
23917         
23918         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23919             
23920             draw: function()
23921             {
23922                 _this.fireEvent('OverlayViewDraw', _this);
23923             },
23924             
23925             onAdd: function()
23926             {
23927                 _this.fireEvent('OverlayViewOnAdd', _this);
23928             },
23929             
23930             onRemove: function()
23931             {
23932                 _this.fireEvent('OverlayViewOnRemove', _this);
23933             },
23934             
23935             show: function(cpx)
23936             {
23937                 _this.fireEvent('OverlayViewShow', _this, cpx);
23938             },
23939             
23940             hide: function()
23941             {
23942                 _this.fireEvent('OverlayViewHide', _this);
23943             }
23944             
23945         });
23946     },
23947     
23948     fromLatLngToContainerPixel: function(event)
23949     {
23950         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23951     },
23952     
23953     isApplied: function() 
23954     {
23955         return this.getGmapContext() == false ? false : true;
23956     },
23957     
23958     getGmapContext: function() 
23959     {
23960         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23961     },
23962     
23963     GMapContext: function() 
23964     {
23965         var position = new google.maps.LatLng(this.latitude, this.longitude);
23966         
23967         var _map = new google.maps.Map(this.el.dom, {
23968             center: position,
23969             zoom: this.zoom,
23970             mapTypeId: this.mapTypeId,
23971             mapTypeControl: this.mapTypeControl,
23972             disableDoubleClickZoom: this.disableDoubleClickZoom,
23973             scrollwheel: this.scrollwheel,
23974             streetViewControl: this.streetViewControl,
23975             locationName: this.locationName,
23976             draggable: this.draggable,
23977             enableAutocomplete: this.enableAutocomplete,
23978             enableReverseGeocode: this.enableReverseGeocode
23979         });
23980         
23981         var _marker = new google.maps.Marker({
23982             position: position,
23983             map: _map,
23984             title: this.markerTitle,
23985             draggable: this.draggable
23986         });
23987         
23988         return {
23989             map: _map,
23990             marker: _marker,
23991             circle: null,
23992             location: position,
23993             radius: this.radius,
23994             locationName: this.locationName,
23995             addressComponents: {
23996                 formatted_address: null,
23997                 addressLine1: null,
23998                 addressLine2: null,
23999                 streetName: null,
24000                 streetNumber: null,
24001                 city: null,
24002                 district: null,
24003                 state: null,
24004                 stateOrProvince: null
24005             },
24006             settings: this,
24007             domContainer: this.el.dom,
24008             geodecoder: new google.maps.Geocoder()
24009         };
24010     },
24011     
24012     drawCircle: function(center, radius, options) 
24013     {
24014         if (this.gMapContext.circle != null) {
24015             this.gMapContext.circle.setMap(null);
24016         }
24017         if (radius > 0) {
24018             radius *= 1;
24019             options = Roo.apply({}, options, {
24020                 strokeColor: "#0000FF",
24021                 strokeOpacity: .35,
24022                 strokeWeight: 2,
24023                 fillColor: "#0000FF",
24024                 fillOpacity: .2
24025             });
24026             
24027             options.map = this.gMapContext.map;
24028             options.radius = radius;
24029             options.center = center;
24030             this.gMapContext.circle = new google.maps.Circle(options);
24031             return this.gMapContext.circle;
24032         }
24033         
24034         return null;
24035     },
24036     
24037     setPosition: function(location) 
24038     {
24039         this.gMapContext.location = location;
24040         this.gMapContext.marker.setPosition(location);
24041         this.gMapContext.map.panTo(location);
24042         this.drawCircle(location, this.gMapContext.radius, {});
24043         
24044         var _this = this;
24045         
24046         if (this.gMapContext.settings.enableReverseGeocode) {
24047             this.gMapContext.geodecoder.geocode({
24048                 latLng: this.gMapContext.location
24049             }, function(results, status) {
24050                 
24051                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24052                     _this.gMapContext.locationName = results[0].formatted_address;
24053                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24054                     
24055                     _this.fireEvent('positionchanged', this, location);
24056                 }
24057             });
24058             
24059             return;
24060         }
24061         
24062         this.fireEvent('positionchanged', this, location);
24063     },
24064     
24065     resize: function()
24066     {
24067         google.maps.event.trigger(this.gMapContext.map, "resize");
24068         
24069         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24070         
24071         this.fireEvent('resize', this);
24072     },
24073     
24074     setPositionByLatLng: function(latitude, longitude)
24075     {
24076         this.setPosition(new google.maps.LatLng(latitude, longitude));
24077     },
24078     
24079     getCurrentPosition: function() 
24080     {
24081         return {
24082             latitude: this.gMapContext.location.lat(),
24083             longitude: this.gMapContext.location.lng()
24084         };
24085     },
24086     
24087     getAddressName: function() 
24088     {
24089         return this.gMapContext.locationName;
24090     },
24091     
24092     getAddressComponents: function() 
24093     {
24094         return this.gMapContext.addressComponents;
24095     },
24096     
24097     address_component_from_google_geocode: function(address_components) 
24098     {
24099         var result = {};
24100         
24101         for (var i = 0; i < address_components.length; i++) {
24102             var component = address_components[i];
24103             if (component.types.indexOf("postal_code") >= 0) {
24104                 result.postalCode = component.short_name;
24105             } else if (component.types.indexOf("street_number") >= 0) {
24106                 result.streetNumber = component.short_name;
24107             } else if (component.types.indexOf("route") >= 0) {
24108                 result.streetName = component.short_name;
24109             } else if (component.types.indexOf("neighborhood") >= 0) {
24110                 result.city = component.short_name;
24111             } else if (component.types.indexOf("locality") >= 0) {
24112                 result.city = component.short_name;
24113             } else if (component.types.indexOf("sublocality") >= 0) {
24114                 result.district = component.short_name;
24115             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24116                 result.stateOrProvince = component.short_name;
24117             } else if (component.types.indexOf("country") >= 0) {
24118                 result.country = component.short_name;
24119             }
24120         }
24121         
24122         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24123         result.addressLine2 = "";
24124         return result;
24125     },
24126     
24127     setZoomLevel: function(zoom)
24128     {
24129         this.gMapContext.map.setZoom(zoom);
24130     },
24131     
24132     show: function()
24133     {
24134         if(!this.el){
24135             return;
24136         }
24137         
24138         this.el.show();
24139         
24140         this.resize();
24141         
24142         this.fireEvent('show', this);
24143     },
24144     
24145     hide: function()
24146     {
24147         if(!this.el){
24148             return;
24149         }
24150         
24151         this.el.hide();
24152         
24153         this.fireEvent('hide', this);
24154     }
24155     
24156 });
24157
24158 Roo.apply(Roo.bootstrap.LocationPicker, {
24159     
24160     OverlayView : function(map, options)
24161     {
24162         options = options || {};
24163         
24164         this.setMap(map);
24165     }
24166     
24167     
24168 });/*
24169  * - LGPL
24170  *
24171  * Alert
24172  * 
24173  */
24174
24175 /**
24176  * @class Roo.bootstrap.Alert
24177  * @extends Roo.bootstrap.Component
24178  * Bootstrap Alert class
24179  * @cfg {String} title The title of alert
24180  * @cfg {String} html The content of alert
24181  * @cfg {String} weight (  success | info | warning | danger )
24182  * @cfg {String} faicon font-awesomeicon
24183  * 
24184  * @constructor
24185  * Create a new alert
24186  * @param {Object} config The config object
24187  */
24188
24189
24190 Roo.bootstrap.Alert = function(config){
24191     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24192     
24193 };
24194
24195 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24196     
24197     title: '',
24198     html: '',
24199     weight: false,
24200     faicon: false,
24201     
24202     getAutoCreate : function()
24203     {
24204         
24205         var cfg = {
24206             tag : 'div',
24207             cls : 'alert',
24208             cn : [
24209                 {
24210                     tag : 'i',
24211                     cls : 'roo-alert-icon'
24212                     
24213                 },
24214                 {
24215                     tag : 'b',
24216                     cls : 'roo-alert-title',
24217                     html : this.title
24218                 },
24219                 {
24220                     tag : 'span',
24221                     cls : 'roo-alert-text',
24222                     html : this.html
24223                 }
24224             ]
24225         };
24226         
24227         if(this.faicon){
24228             cfg.cn[0].cls += ' fa ' + this.faicon;
24229         }
24230         
24231         if(this.weight){
24232             cfg.cls += ' alert-' + this.weight;
24233         }
24234         
24235         return cfg;
24236     },
24237     
24238     initEvents: function() 
24239     {
24240         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24241     },
24242     
24243     setTitle : function(str)
24244     {
24245         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24246     },
24247     
24248     setText : function(str)
24249     {
24250         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24251     },
24252     
24253     setWeight : function(weight)
24254     {
24255         if(this.weight){
24256             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24257         }
24258         
24259         this.weight = weight;
24260         
24261         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24262     },
24263     
24264     setIcon : function(icon)
24265     {
24266         if(this.faicon){
24267             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24268         }
24269         
24270         this.faicon = icon;
24271         
24272         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24273     },
24274     
24275     hide: function() 
24276     {
24277         this.el.hide();   
24278     },
24279     
24280     show: function() 
24281     {  
24282         this.el.show();   
24283     }
24284     
24285 });
24286
24287  
24288 /*
24289 * Licence: LGPL
24290 */
24291
24292 /**
24293  * @class Roo.bootstrap.UploadCropbox
24294  * @extends Roo.bootstrap.Component
24295  * Bootstrap UploadCropbox class
24296  * @cfg {String} emptyText show when image has been loaded
24297  * @cfg {String} rotateNotify show when image too small to rotate
24298  * @cfg {Number} errorTimeout default 3000
24299  * @cfg {Number} minWidth default 300
24300  * @cfg {Number} minHeight default 300
24301  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24302  * @cfg {Boolean} isDocument (true|false) default false
24303  * @cfg {String} url action url
24304  * @cfg {String} paramName default 'imageUpload'
24305  * @cfg {String} method default POST
24306  * @cfg {Boolean} loadMask (true|false) default true
24307  * @cfg {Boolean} loadingText default 'Loading...'
24308  * 
24309  * @constructor
24310  * Create a new UploadCropbox
24311  * @param {Object} config The config object
24312  */
24313
24314 Roo.bootstrap.UploadCropbox = function(config){
24315     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24316     
24317     this.addEvents({
24318         /**
24319          * @event beforeselectfile
24320          * Fire before select file
24321          * @param {Roo.bootstrap.UploadCropbox} this
24322          */
24323         "beforeselectfile" : true,
24324         /**
24325          * @event initial
24326          * Fire after initEvent
24327          * @param {Roo.bootstrap.UploadCropbox} this
24328          */
24329         "initial" : true,
24330         /**
24331          * @event crop
24332          * Fire after initEvent
24333          * @param {Roo.bootstrap.UploadCropbox} this
24334          * @param {String} data
24335          */
24336         "crop" : true,
24337         /**
24338          * @event prepare
24339          * Fire when preparing the file data
24340          * @param {Roo.bootstrap.UploadCropbox} this
24341          * @param {Object} file
24342          */
24343         "prepare" : true,
24344         /**
24345          * @event exception
24346          * Fire when get exception
24347          * @param {Roo.bootstrap.UploadCropbox} this
24348          * @param {XMLHttpRequest} xhr
24349          */
24350         "exception" : true,
24351         /**
24352          * @event beforeloadcanvas
24353          * Fire before load the canvas
24354          * @param {Roo.bootstrap.UploadCropbox} this
24355          * @param {String} src
24356          */
24357         "beforeloadcanvas" : true,
24358         /**
24359          * @event trash
24360          * Fire when trash image
24361          * @param {Roo.bootstrap.UploadCropbox} this
24362          */
24363         "trash" : true,
24364         /**
24365          * @event download
24366          * Fire when download the image
24367          * @param {Roo.bootstrap.UploadCropbox} this
24368          */
24369         "download" : true,
24370         /**
24371          * @event footerbuttonclick
24372          * Fire when footerbuttonclick
24373          * @param {Roo.bootstrap.UploadCropbox} this
24374          * @param {String} type
24375          */
24376         "footerbuttonclick" : true,
24377         /**
24378          * @event resize
24379          * Fire when resize
24380          * @param {Roo.bootstrap.UploadCropbox} this
24381          */
24382         "resize" : true,
24383         /**
24384          * @event rotate
24385          * Fire when rotate the image
24386          * @param {Roo.bootstrap.UploadCropbox} this
24387          * @param {String} pos
24388          */
24389         "rotate" : true,
24390         /**
24391          * @event inspect
24392          * Fire when inspect the file
24393          * @param {Roo.bootstrap.UploadCropbox} this
24394          * @param {Object} file
24395          */
24396         "inspect" : true,
24397         /**
24398          * @event upload
24399          * Fire when xhr upload the file
24400          * @param {Roo.bootstrap.UploadCropbox} this
24401          * @param {Object} data
24402          */
24403         "upload" : true,
24404         /**
24405          * @event arrange
24406          * Fire when arrange the file data
24407          * @param {Roo.bootstrap.UploadCropbox} this
24408          * @param {Object} formData
24409          */
24410         "arrange" : true
24411     });
24412     
24413     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24414 };
24415
24416 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24417     
24418     emptyText : 'Click to upload image',
24419     rotateNotify : 'Image is too small to rotate',
24420     errorTimeout : 3000,
24421     scale : 0,
24422     baseScale : 1,
24423     rotate : 0,
24424     dragable : false,
24425     pinching : false,
24426     mouseX : 0,
24427     mouseY : 0,
24428     cropData : false,
24429     minWidth : 300,
24430     minHeight : 300,
24431     file : false,
24432     exif : {},
24433     baseRotate : 1,
24434     cropType : 'image/jpeg',
24435     buttons : false,
24436     canvasLoaded : false,
24437     isDocument : false,
24438     method : 'POST',
24439     paramName : 'imageUpload',
24440     loadMask : true,
24441     loadingText : 'Loading...',
24442     maskEl : false,
24443     
24444     getAutoCreate : function()
24445     {
24446         var cfg = {
24447             tag : 'div',
24448             cls : 'roo-upload-cropbox',
24449             cn : [
24450                 {
24451                     tag : 'input',
24452                     cls : 'roo-upload-cropbox-selector',
24453                     type : 'file'
24454                 },
24455                 {
24456                     tag : 'div',
24457                     cls : 'roo-upload-cropbox-body',
24458                     style : 'cursor:pointer',
24459                     cn : [
24460                         {
24461                             tag : 'div',
24462                             cls : 'roo-upload-cropbox-preview'
24463                         },
24464                         {
24465                             tag : 'div',
24466                             cls : 'roo-upload-cropbox-thumb'
24467                         },
24468                         {
24469                             tag : 'div',
24470                             cls : 'roo-upload-cropbox-empty-notify',
24471                             html : this.emptyText
24472                         },
24473                         {
24474                             tag : 'div',
24475                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24476                             html : this.rotateNotify
24477                         }
24478                     ]
24479                 },
24480                 {
24481                     tag : 'div',
24482                     cls : 'roo-upload-cropbox-footer',
24483                     cn : {
24484                         tag : 'div',
24485                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24486                         cn : []
24487                     }
24488                 }
24489             ]
24490         };
24491         
24492         return cfg;
24493     },
24494     
24495     onRender : function(ct, position)
24496     {
24497         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24498         
24499         if (this.buttons.length) {
24500             
24501             Roo.each(this.buttons, function(bb) {
24502                 
24503                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24504                 
24505                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24506                 
24507             }, this);
24508         }
24509         
24510         if(this.loadMask){
24511             this.maskEl = this.el;
24512         }
24513     },
24514     
24515     initEvents : function()
24516     {
24517         this.urlAPI = (window.createObjectURL && window) || 
24518                                 (window.URL && URL.revokeObjectURL && URL) || 
24519                                 (window.webkitURL && webkitURL);
24520                         
24521         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24522         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24523         
24524         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24525         this.selectorEl.hide();
24526         
24527         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24528         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24529         
24530         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24531         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24532         this.thumbEl.hide();
24533         
24534         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24535         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24536         
24537         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24538         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24539         this.errorEl.hide();
24540         
24541         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24542         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24543         this.footerEl.hide();
24544         
24545         this.setThumbBoxSize();
24546         
24547         this.bind();
24548         
24549         this.resize();
24550         
24551         this.fireEvent('initial', this);
24552     },
24553
24554     bind : function()
24555     {
24556         var _this = this;
24557         
24558         window.addEventListener("resize", function() { _this.resize(); } );
24559         
24560         this.bodyEl.on('click', this.beforeSelectFile, this);
24561         
24562         if(Roo.isTouch){
24563             this.bodyEl.on('touchstart', this.onTouchStart, this);
24564             this.bodyEl.on('touchmove', this.onTouchMove, this);
24565             this.bodyEl.on('touchend', this.onTouchEnd, this);
24566         }
24567         
24568         if(!Roo.isTouch){
24569             this.bodyEl.on('mousedown', this.onMouseDown, this);
24570             this.bodyEl.on('mousemove', this.onMouseMove, this);
24571             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24572             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24573             Roo.get(document).on('mouseup', this.onMouseUp, this);
24574         }
24575         
24576         this.selectorEl.on('change', this.onFileSelected, this);
24577     },
24578     
24579     reset : function()
24580     {    
24581         this.scale = 0;
24582         this.baseScale = 1;
24583         this.rotate = 0;
24584         this.baseRotate = 1;
24585         this.dragable = false;
24586         this.pinching = false;
24587         this.mouseX = 0;
24588         this.mouseY = 0;
24589         this.cropData = false;
24590         this.notifyEl.dom.innerHTML = this.emptyText;
24591         
24592         this.selectorEl.dom.value = '';
24593         
24594     },
24595     
24596     resize : function()
24597     {
24598         if(this.fireEvent('resize', this) != false){
24599             this.setThumbBoxPosition();
24600             this.setCanvasPosition();
24601         }
24602     },
24603     
24604     onFooterButtonClick : function(e, el, o, type)
24605     {
24606         switch (type) {
24607             case 'rotate-left' :
24608                 this.onRotateLeft(e);
24609                 break;
24610             case 'rotate-right' :
24611                 this.onRotateRight(e);
24612                 break;
24613             case 'picture' :
24614                 this.beforeSelectFile(e);
24615                 break;
24616             case 'trash' :
24617                 this.trash(e);
24618                 break;
24619             case 'crop' :
24620                 this.crop(e);
24621                 break;
24622             case 'download' :
24623                 this.download(e);
24624                 break;
24625             default :
24626                 break;
24627         }
24628         
24629         this.fireEvent('footerbuttonclick', this, type);
24630     },
24631     
24632     beforeSelectFile : function(e)
24633     {
24634         e.preventDefault();
24635         
24636         if(this.fireEvent('beforeselectfile', this) != false){
24637             this.selectorEl.dom.click();
24638         }
24639     },
24640     
24641     onFileSelected : function(e)
24642     {
24643         e.preventDefault();
24644         
24645         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24646             return;
24647         }
24648         
24649         var file = this.selectorEl.dom.files[0];
24650         
24651         if(this.fireEvent('inspect', this, file) != false){
24652             this.prepare(file);
24653         }
24654         
24655     },
24656     
24657     trash : function(e)
24658     {
24659         this.fireEvent('trash', this);
24660     },
24661     
24662     download : function(e)
24663     {
24664         this.fireEvent('download', this);
24665     },
24666     
24667     loadCanvas : function(src)
24668     {   
24669         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24670             
24671             this.reset();
24672             
24673             this.imageEl = document.createElement('img');
24674             
24675             var _this = this;
24676             
24677             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24678             
24679             this.imageEl.src = src;
24680         }
24681     },
24682     
24683     onLoadCanvas : function()
24684     {   
24685         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24686         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24687         
24688         this.bodyEl.un('click', this.beforeSelectFile, this);
24689         
24690         this.notifyEl.hide();
24691         this.thumbEl.show();
24692         this.footerEl.show();
24693         
24694         this.baseRotateLevel();
24695         
24696         if(this.isDocument){
24697             this.setThumbBoxSize();
24698         }
24699         
24700         this.setThumbBoxPosition();
24701         
24702         this.baseScaleLevel();
24703         
24704         this.draw();
24705         
24706         this.resize();
24707         
24708         this.canvasLoaded = true;
24709         
24710         if(this.loadMask){
24711             this.maskEl.unmask();
24712         }
24713         
24714     },
24715     
24716     setCanvasPosition : function()
24717     {   
24718         if(!this.canvasEl){
24719             return;
24720         }
24721         
24722         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24723         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24724         
24725         this.previewEl.setLeft(pw);
24726         this.previewEl.setTop(ph);
24727         
24728     },
24729     
24730     onMouseDown : function(e)
24731     {   
24732         e.stopEvent();
24733         
24734         this.dragable = true;
24735         this.pinching = false;
24736         
24737         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24738             this.dragable = false;
24739             return;
24740         }
24741         
24742         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24743         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24744         
24745     },
24746     
24747     onMouseMove : function(e)
24748     {   
24749         e.stopEvent();
24750         
24751         if(!this.canvasLoaded){
24752             return;
24753         }
24754         
24755         if (!this.dragable){
24756             return;
24757         }
24758         
24759         var minX = Math.ceil(this.thumbEl.getLeft(true));
24760         var minY = Math.ceil(this.thumbEl.getTop(true));
24761         
24762         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24763         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24764         
24765         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24766         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24767         
24768         x = x - this.mouseX;
24769         y = y - this.mouseY;
24770         
24771         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24772         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24773         
24774         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24775         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24776         
24777         this.previewEl.setLeft(bgX);
24778         this.previewEl.setTop(bgY);
24779         
24780         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24781         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24782     },
24783     
24784     onMouseUp : function(e)
24785     {   
24786         e.stopEvent();
24787         
24788         this.dragable = false;
24789     },
24790     
24791     onMouseWheel : function(e)
24792     {   
24793         e.stopEvent();
24794         
24795         this.startScale = this.scale;
24796         
24797         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24798         
24799         if(!this.zoomable()){
24800             this.scale = this.startScale;
24801             return;
24802         }
24803         
24804         this.draw();
24805         
24806         return;
24807     },
24808     
24809     zoomable : function()
24810     {
24811         var minScale = this.thumbEl.getWidth() / this.minWidth;
24812         
24813         if(this.minWidth < this.minHeight){
24814             minScale = this.thumbEl.getHeight() / this.minHeight;
24815         }
24816         
24817         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24818         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24819         
24820         if(
24821                 this.isDocument &&
24822                 (this.rotate == 0 || this.rotate == 180) && 
24823                 (
24824                     width > this.imageEl.OriginWidth || 
24825                     height > this.imageEl.OriginHeight ||
24826                     (width < this.minWidth && height < this.minHeight)
24827                 )
24828         ){
24829             return false;
24830         }
24831         
24832         if(
24833                 this.isDocument &&
24834                 (this.rotate == 90 || this.rotate == 270) && 
24835                 (
24836                     width > this.imageEl.OriginWidth || 
24837                     height > this.imageEl.OriginHeight ||
24838                     (width < this.minHeight && height < this.minWidth)
24839                 )
24840         ){
24841             return false;
24842         }
24843         
24844         if(
24845                 !this.isDocument &&
24846                 (this.rotate == 0 || this.rotate == 180) && 
24847                 (
24848                     width < this.minWidth || 
24849                     width > this.imageEl.OriginWidth || 
24850                     height < this.minHeight || 
24851                     height > this.imageEl.OriginHeight
24852                 )
24853         ){
24854             return false;
24855         }
24856         
24857         if(
24858                 !this.isDocument &&
24859                 (this.rotate == 90 || this.rotate == 270) && 
24860                 (
24861                     width < this.minHeight || 
24862                     width > this.imageEl.OriginWidth || 
24863                     height < this.minWidth || 
24864                     height > this.imageEl.OriginHeight
24865                 )
24866         ){
24867             return false;
24868         }
24869         
24870         return true;
24871         
24872     },
24873     
24874     onRotateLeft : function(e)
24875     {   
24876         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24877             
24878             var minScale = this.thumbEl.getWidth() / this.minWidth;
24879             
24880             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24881             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24882             
24883             this.startScale = this.scale;
24884             
24885             while (this.getScaleLevel() < minScale){
24886             
24887                 this.scale = this.scale + 1;
24888                 
24889                 if(!this.zoomable()){
24890                     break;
24891                 }
24892                 
24893                 if(
24894                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24895                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24896                 ){
24897                     continue;
24898                 }
24899                 
24900                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24901
24902                 this.draw();
24903                 
24904                 return;
24905             }
24906             
24907             this.scale = this.startScale;
24908             
24909             this.onRotateFail();
24910             
24911             return false;
24912         }
24913         
24914         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24915
24916         if(this.isDocument){
24917             this.setThumbBoxSize();
24918             this.setThumbBoxPosition();
24919             this.setCanvasPosition();
24920         }
24921         
24922         this.draw();
24923         
24924         this.fireEvent('rotate', this, 'left');
24925         
24926     },
24927     
24928     onRotateRight : function(e)
24929     {
24930         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24931             
24932             var minScale = this.thumbEl.getWidth() / this.minWidth;
24933         
24934             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24935             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24936             
24937             this.startScale = this.scale;
24938             
24939             while (this.getScaleLevel() < minScale){
24940             
24941                 this.scale = this.scale + 1;
24942                 
24943                 if(!this.zoomable()){
24944                     break;
24945                 }
24946                 
24947                 if(
24948                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24949                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24950                 ){
24951                     continue;
24952                 }
24953                 
24954                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24955
24956                 this.draw();
24957                 
24958                 return;
24959             }
24960             
24961             this.scale = this.startScale;
24962             
24963             this.onRotateFail();
24964             
24965             return false;
24966         }
24967         
24968         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24969
24970         if(this.isDocument){
24971             this.setThumbBoxSize();
24972             this.setThumbBoxPosition();
24973             this.setCanvasPosition();
24974         }
24975         
24976         this.draw();
24977         
24978         this.fireEvent('rotate', this, 'right');
24979     },
24980     
24981     onRotateFail : function()
24982     {
24983         this.errorEl.show(true);
24984         
24985         var _this = this;
24986         
24987         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24988     },
24989     
24990     draw : function()
24991     {
24992         this.previewEl.dom.innerHTML = '';
24993         
24994         var canvasEl = document.createElement("canvas");
24995         
24996         var contextEl = canvasEl.getContext("2d");
24997         
24998         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24999         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25000         var center = this.imageEl.OriginWidth / 2;
25001         
25002         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25003             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25004             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25005             center = this.imageEl.OriginHeight / 2;
25006         }
25007         
25008         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25009         
25010         contextEl.translate(center, center);
25011         contextEl.rotate(this.rotate * Math.PI / 180);
25012
25013         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25014         
25015         this.canvasEl = document.createElement("canvas");
25016         
25017         this.contextEl = this.canvasEl.getContext("2d");
25018         
25019         switch (this.rotate) {
25020             case 0 :
25021                 
25022                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25023                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25024                 
25025                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25026                 
25027                 break;
25028             case 90 : 
25029                 
25030                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25031                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25032                 
25033                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25034                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25035                     break;
25036                 }
25037                 
25038                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25039                 
25040                 break;
25041             case 180 :
25042                 
25043                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25044                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25045                 
25046                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25047                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25048                     break;
25049                 }
25050                 
25051                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25052                 
25053                 break;
25054             case 270 :
25055                 
25056                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25057                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25058         
25059                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25060                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25061                     break;
25062                 }
25063                 
25064                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25065                 
25066                 break;
25067             default : 
25068                 break;
25069         }
25070         
25071         this.previewEl.appendChild(this.canvasEl);
25072         
25073         this.setCanvasPosition();
25074     },
25075     
25076     crop : function()
25077     {
25078         if(!this.canvasLoaded){
25079             return;
25080         }
25081         
25082         var imageCanvas = document.createElement("canvas");
25083         
25084         var imageContext = imageCanvas.getContext("2d");
25085         
25086         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25087         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25088         
25089         var center = imageCanvas.width / 2;
25090         
25091         imageContext.translate(center, center);
25092         
25093         imageContext.rotate(this.rotate * Math.PI / 180);
25094         
25095         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25096         
25097         var canvas = document.createElement("canvas");
25098         
25099         var context = canvas.getContext("2d");
25100                 
25101         canvas.width = this.minWidth;
25102         canvas.height = this.minHeight;
25103
25104         switch (this.rotate) {
25105             case 0 :
25106                 
25107                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25108                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25109                 
25110                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25111                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25112                 
25113                 var targetWidth = this.minWidth - 2 * x;
25114                 var targetHeight = this.minHeight - 2 * y;
25115                 
25116                 var scale = 1;
25117                 
25118                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25119                     scale = targetWidth / width;
25120                 }
25121                 
25122                 if(x > 0 && y == 0){
25123                     scale = targetHeight / height;
25124                 }
25125                 
25126                 if(x > 0 && y > 0){
25127                     scale = targetWidth / width;
25128                     
25129                     if(width < height){
25130                         scale = targetHeight / height;
25131                     }
25132                 }
25133                 
25134                 context.scale(scale, scale);
25135                 
25136                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25137                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25138
25139                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25140                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25141
25142                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25143                 
25144                 break;
25145             case 90 : 
25146                 
25147                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25148                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25149                 
25150                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25151                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25152                 
25153                 var targetWidth = this.minWidth - 2 * x;
25154                 var targetHeight = this.minHeight - 2 * y;
25155                 
25156                 var scale = 1;
25157                 
25158                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25159                     scale = targetWidth / width;
25160                 }
25161                 
25162                 if(x > 0 && y == 0){
25163                     scale = targetHeight / height;
25164                 }
25165                 
25166                 if(x > 0 && y > 0){
25167                     scale = targetWidth / width;
25168                     
25169                     if(width < height){
25170                         scale = targetHeight / height;
25171                     }
25172                 }
25173                 
25174                 context.scale(scale, scale);
25175                 
25176                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25177                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25178
25179                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25180                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25181                 
25182                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25183                 
25184                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25185                 
25186                 break;
25187             case 180 :
25188                 
25189                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25190                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25191                 
25192                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25193                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25194                 
25195                 var targetWidth = this.minWidth - 2 * x;
25196                 var targetHeight = this.minHeight - 2 * y;
25197                 
25198                 var scale = 1;
25199                 
25200                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25201                     scale = targetWidth / width;
25202                 }
25203                 
25204                 if(x > 0 && y == 0){
25205                     scale = targetHeight / height;
25206                 }
25207                 
25208                 if(x > 0 && y > 0){
25209                     scale = targetWidth / width;
25210                     
25211                     if(width < height){
25212                         scale = targetHeight / height;
25213                     }
25214                 }
25215                 
25216                 context.scale(scale, scale);
25217                 
25218                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25219                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25220
25221                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25222                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25223
25224                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25225                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25226                 
25227                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25228                 
25229                 break;
25230             case 270 :
25231                 
25232                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25233                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25234                 
25235                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25236                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25237                 
25238                 var targetWidth = this.minWidth - 2 * x;
25239                 var targetHeight = this.minHeight - 2 * y;
25240                 
25241                 var scale = 1;
25242                 
25243                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25244                     scale = targetWidth / width;
25245                 }
25246                 
25247                 if(x > 0 && y == 0){
25248                     scale = targetHeight / height;
25249                 }
25250                 
25251                 if(x > 0 && y > 0){
25252                     scale = targetWidth / width;
25253                     
25254                     if(width < height){
25255                         scale = targetHeight / height;
25256                     }
25257                 }
25258                 
25259                 context.scale(scale, scale);
25260                 
25261                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25262                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25263
25264                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25265                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25266                 
25267                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25268                 
25269                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25270                 
25271                 break;
25272             default : 
25273                 break;
25274         }
25275         
25276         this.cropData = canvas.toDataURL(this.cropType);
25277         
25278         if(this.fireEvent('crop', this, this.cropData) !== false){
25279             this.process(this.file, this.cropData);
25280         }
25281         
25282         return;
25283         
25284     },
25285     
25286     setThumbBoxSize : function()
25287     {
25288         var width, height;
25289         
25290         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25291             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25292             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25293             
25294             this.minWidth = width;
25295             this.minHeight = height;
25296             
25297             if(this.rotate == 90 || this.rotate == 270){
25298                 this.minWidth = height;
25299                 this.minHeight = width;
25300             }
25301         }
25302         
25303         height = 300;
25304         width = Math.ceil(this.minWidth * height / this.minHeight);
25305         
25306         if(this.minWidth > this.minHeight){
25307             width = 300;
25308             height = Math.ceil(this.minHeight * width / this.minWidth);
25309         }
25310         
25311         this.thumbEl.setStyle({
25312             width : width + 'px',
25313             height : height + 'px'
25314         });
25315
25316         return;
25317             
25318     },
25319     
25320     setThumbBoxPosition : function()
25321     {
25322         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25323         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25324         
25325         this.thumbEl.setLeft(x);
25326         this.thumbEl.setTop(y);
25327         
25328     },
25329     
25330     baseRotateLevel : function()
25331     {
25332         this.baseRotate = 1;
25333         
25334         if(
25335                 typeof(this.exif) != 'undefined' &&
25336                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25337                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25338         ){
25339             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25340         }
25341         
25342         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25343         
25344     },
25345     
25346     baseScaleLevel : function()
25347     {
25348         var width, height;
25349         
25350         if(this.isDocument){
25351             
25352             if(this.baseRotate == 6 || this.baseRotate == 8){
25353             
25354                 height = this.thumbEl.getHeight();
25355                 this.baseScale = height / this.imageEl.OriginWidth;
25356
25357                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25358                     width = this.thumbEl.getWidth();
25359                     this.baseScale = width / this.imageEl.OriginHeight;
25360                 }
25361
25362                 return;
25363             }
25364
25365             height = this.thumbEl.getHeight();
25366             this.baseScale = height / this.imageEl.OriginHeight;
25367
25368             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25369                 width = this.thumbEl.getWidth();
25370                 this.baseScale = width / this.imageEl.OriginWidth;
25371             }
25372
25373             return;
25374         }
25375         
25376         if(this.baseRotate == 6 || this.baseRotate == 8){
25377             
25378             width = this.thumbEl.getHeight();
25379             this.baseScale = width / this.imageEl.OriginHeight;
25380             
25381             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25382                 height = this.thumbEl.getWidth();
25383                 this.baseScale = height / this.imageEl.OriginHeight;
25384             }
25385             
25386             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25387                 height = this.thumbEl.getWidth();
25388                 this.baseScale = height / this.imageEl.OriginHeight;
25389                 
25390                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25391                     width = this.thumbEl.getHeight();
25392                     this.baseScale = width / this.imageEl.OriginWidth;
25393                 }
25394             }
25395             
25396             return;
25397         }
25398         
25399         width = this.thumbEl.getWidth();
25400         this.baseScale = width / this.imageEl.OriginWidth;
25401         
25402         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25403             height = this.thumbEl.getHeight();
25404             this.baseScale = height / this.imageEl.OriginHeight;
25405         }
25406         
25407         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25408             
25409             height = this.thumbEl.getHeight();
25410             this.baseScale = height / this.imageEl.OriginHeight;
25411             
25412             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25413                 width = this.thumbEl.getWidth();
25414                 this.baseScale = width / this.imageEl.OriginWidth;
25415             }
25416             
25417         }
25418         
25419         return;
25420     },
25421     
25422     getScaleLevel : function()
25423     {
25424         return this.baseScale * Math.pow(1.1, this.scale);
25425     },
25426     
25427     onTouchStart : function(e)
25428     {
25429         if(!this.canvasLoaded){
25430             this.beforeSelectFile(e);
25431             return;
25432         }
25433         
25434         var touches = e.browserEvent.touches;
25435         
25436         if(!touches){
25437             return;
25438         }
25439         
25440         if(touches.length == 1){
25441             this.onMouseDown(e);
25442             return;
25443         }
25444         
25445         if(touches.length != 2){
25446             return;
25447         }
25448         
25449         var coords = [];
25450         
25451         for(var i = 0, finger; finger = touches[i]; i++){
25452             coords.push(finger.pageX, finger.pageY);
25453         }
25454         
25455         var x = Math.pow(coords[0] - coords[2], 2);
25456         var y = Math.pow(coords[1] - coords[3], 2);
25457         
25458         this.startDistance = Math.sqrt(x + y);
25459         
25460         this.startScale = this.scale;
25461         
25462         this.pinching = true;
25463         this.dragable = false;
25464         
25465     },
25466     
25467     onTouchMove : function(e)
25468     {
25469         if(!this.pinching && !this.dragable){
25470             return;
25471         }
25472         
25473         var touches = e.browserEvent.touches;
25474         
25475         if(!touches){
25476             return;
25477         }
25478         
25479         if(this.dragable){
25480             this.onMouseMove(e);
25481             return;
25482         }
25483         
25484         var coords = [];
25485         
25486         for(var i = 0, finger; finger = touches[i]; i++){
25487             coords.push(finger.pageX, finger.pageY);
25488         }
25489         
25490         var x = Math.pow(coords[0] - coords[2], 2);
25491         var y = Math.pow(coords[1] - coords[3], 2);
25492         
25493         this.endDistance = Math.sqrt(x + y);
25494         
25495         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25496         
25497         if(!this.zoomable()){
25498             this.scale = this.startScale;
25499             return;
25500         }
25501         
25502         this.draw();
25503         
25504     },
25505     
25506     onTouchEnd : function(e)
25507     {
25508         this.pinching = false;
25509         this.dragable = false;
25510         
25511     },
25512     
25513     process : function(file, crop)
25514     {
25515         if(this.loadMask){
25516             this.maskEl.mask(this.loadingText);
25517         }
25518         
25519         this.xhr = new XMLHttpRequest();
25520         
25521         file.xhr = this.xhr;
25522
25523         this.xhr.open(this.method, this.url, true);
25524         
25525         var headers = {
25526             "Accept": "application/json",
25527             "Cache-Control": "no-cache",
25528             "X-Requested-With": "XMLHttpRequest"
25529         };
25530         
25531         for (var headerName in headers) {
25532             var headerValue = headers[headerName];
25533             if (headerValue) {
25534                 this.xhr.setRequestHeader(headerName, headerValue);
25535             }
25536         }
25537         
25538         var _this = this;
25539         
25540         this.xhr.onload = function()
25541         {
25542             _this.xhrOnLoad(_this.xhr);
25543         }
25544         
25545         this.xhr.onerror = function()
25546         {
25547             _this.xhrOnError(_this.xhr);
25548         }
25549         
25550         var formData = new FormData();
25551
25552         formData.append('returnHTML', 'NO');
25553         
25554         if(crop){
25555             formData.append('crop', crop);
25556         }
25557         
25558         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25559             formData.append(this.paramName, file, file.name);
25560         }
25561         
25562         if(typeof(file.filename) != 'undefined'){
25563             formData.append('filename', file.filename);
25564         }
25565         
25566         if(typeof(file.mimetype) != 'undefined'){
25567             formData.append('mimetype', file.mimetype);
25568         }
25569         
25570         if(this.fireEvent('arrange', this, formData) != false){
25571             this.xhr.send(formData);
25572         };
25573     },
25574     
25575     xhrOnLoad : function(xhr)
25576     {
25577         if(this.loadMask){
25578             this.maskEl.unmask();
25579         }
25580         
25581         if (xhr.readyState !== 4) {
25582             this.fireEvent('exception', this, xhr);
25583             return;
25584         }
25585
25586         var response = Roo.decode(xhr.responseText);
25587         
25588         if(!response.success){
25589             this.fireEvent('exception', this, xhr);
25590             return;
25591         }
25592         
25593         var response = Roo.decode(xhr.responseText);
25594         
25595         this.fireEvent('upload', this, response);
25596         
25597     },
25598     
25599     xhrOnError : function()
25600     {
25601         if(this.loadMask){
25602             this.maskEl.unmask();
25603         }
25604         
25605         Roo.log('xhr on error');
25606         
25607         var response = Roo.decode(xhr.responseText);
25608           
25609         Roo.log(response);
25610         
25611     },
25612     
25613     prepare : function(file)
25614     {   
25615         if(this.loadMask){
25616             this.maskEl.mask(this.loadingText);
25617         }
25618         
25619         this.file = false;
25620         this.exif = {};
25621         
25622         if(typeof(file) === 'string'){
25623             this.loadCanvas(file);
25624             return;
25625         }
25626         
25627         if(!file || !this.urlAPI){
25628             return;
25629         }
25630         
25631         this.file = file;
25632         this.cropType = file.type;
25633         
25634         var _this = this;
25635         
25636         if(this.fireEvent('prepare', this, this.file) != false){
25637             
25638             var reader = new FileReader();
25639             
25640             reader.onload = function (e) {
25641                 if (e.target.error) {
25642                     Roo.log(e.target.error);
25643                     return;
25644                 }
25645                 
25646                 var buffer = e.target.result,
25647                     dataView = new DataView(buffer),
25648                     offset = 2,
25649                     maxOffset = dataView.byteLength - 4,
25650                     markerBytes,
25651                     markerLength;
25652                 
25653                 if (dataView.getUint16(0) === 0xffd8) {
25654                     while (offset < maxOffset) {
25655                         markerBytes = dataView.getUint16(offset);
25656                         
25657                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25658                             markerLength = dataView.getUint16(offset + 2) + 2;
25659                             if (offset + markerLength > dataView.byteLength) {
25660                                 Roo.log('Invalid meta data: Invalid segment size.');
25661                                 break;
25662                             }
25663                             
25664                             if(markerBytes == 0xffe1){
25665                                 _this.parseExifData(
25666                                     dataView,
25667                                     offset,
25668                                     markerLength
25669                                 );
25670                             }
25671                             
25672                             offset += markerLength;
25673                             
25674                             continue;
25675                         }
25676                         
25677                         break;
25678                     }
25679                     
25680                 }
25681                 
25682                 var url = _this.urlAPI.createObjectURL(_this.file);
25683                 
25684                 _this.loadCanvas(url);
25685                 
25686                 return;
25687             }
25688             
25689             reader.readAsArrayBuffer(this.file);
25690             
25691         }
25692         
25693     },
25694     
25695     parseExifData : function(dataView, offset, length)
25696     {
25697         var tiffOffset = offset + 10,
25698             littleEndian,
25699             dirOffset;
25700     
25701         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25702             // No Exif data, might be XMP data instead
25703             return;
25704         }
25705         
25706         // Check for the ASCII code for "Exif" (0x45786966):
25707         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25708             // No Exif data, might be XMP data instead
25709             return;
25710         }
25711         if (tiffOffset + 8 > dataView.byteLength) {
25712             Roo.log('Invalid Exif data: Invalid segment size.');
25713             return;
25714         }
25715         // Check for the two null bytes:
25716         if (dataView.getUint16(offset + 8) !== 0x0000) {
25717             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25718             return;
25719         }
25720         // Check the byte alignment:
25721         switch (dataView.getUint16(tiffOffset)) {
25722         case 0x4949:
25723             littleEndian = true;
25724             break;
25725         case 0x4D4D:
25726             littleEndian = false;
25727             break;
25728         default:
25729             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25730             return;
25731         }
25732         // Check for the TIFF tag marker (0x002A):
25733         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25734             Roo.log('Invalid Exif data: Missing TIFF marker.');
25735             return;
25736         }
25737         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25738         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25739         
25740         this.parseExifTags(
25741             dataView,
25742             tiffOffset,
25743             tiffOffset + dirOffset,
25744             littleEndian
25745         );
25746     },
25747     
25748     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25749     {
25750         var tagsNumber,
25751             dirEndOffset,
25752             i;
25753         if (dirOffset + 6 > dataView.byteLength) {
25754             Roo.log('Invalid Exif data: Invalid directory offset.');
25755             return;
25756         }
25757         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25758         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25759         if (dirEndOffset + 4 > dataView.byteLength) {
25760             Roo.log('Invalid Exif data: Invalid directory size.');
25761             return;
25762         }
25763         for (i = 0; i < tagsNumber; i += 1) {
25764             this.parseExifTag(
25765                 dataView,
25766                 tiffOffset,
25767                 dirOffset + 2 + 12 * i, // tag offset
25768                 littleEndian
25769             );
25770         }
25771         // Return the offset to the next directory:
25772         return dataView.getUint32(dirEndOffset, littleEndian);
25773     },
25774     
25775     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25776     {
25777         var tag = dataView.getUint16(offset, littleEndian);
25778         
25779         this.exif[tag] = this.getExifValue(
25780             dataView,
25781             tiffOffset,
25782             offset,
25783             dataView.getUint16(offset + 2, littleEndian), // tag type
25784             dataView.getUint32(offset + 4, littleEndian), // tag length
25785             littleEndian
25786         );
25787     },
25788     
25789     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25790     {
25791         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25792             tagSize,
25793             dataOffset,
25794             values,
25795             i,
25796             str,
25797             c;
25798     
25799         if (!tagType) {
25800             Roo.log('Invalid Exif data: Invalid tag type.');
25801             return;
25802         }
25803         
25804         tagSize = tagType.size * length;
25805         // Determine if the value is contained in the dataOffset bytes,
25806         // or if the value at the dataOffset is a pointer to the actual data:
25807         dataOffset = tagSize > 4 ?
25808                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25809         if (dataOffset + tagSize > dataView.byteLength) {
25810             Roo.log('Invalid Exif data: Invalid data offset.');
25811             return;
25812         }
25813         if (length === 1) {
25814             return tagType.getValue(dataView, dataOffset, littleEndian);
25815         }
25816         values = [];
25817         for (i = 0; i < length; i += 1) {
25818             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25819         }
25820         
25821         if (tagType.ascii) {
25822             str = '';
25823             // Concatenate the chars:
25824             for (i = 0; i < values.length; i += 1) {
25825                 c = values[i];
25826                 // Ignore the terminating NULL byte(s):
25827                 if (c === '\u0000') {
25828                     break;
25829                 }
25830                 str += c;
25831             }
25832             return str;
25833         }
25834         return values;
25835     }
25836     
25837 });
25838
25839 Roo.apply(Roo.bootstrap.UploadCropbox, {
25840     tags : {
25841         'Orientation': 0x0112
25842     },
25843     
25844     Orientation: {
25845             1: 0, //'top-left',
25846 //            2: 'top-right',
25847             3: 180, //'bottom-right',
25848 //            4: 'bottom-left',
25849 //            5: 'left-top',
25850             6: 90, //'right-top',
25851 //            7: 'right-bottom',
25852             8: 270 //'left-bottom'
25853     },
25854     
25855     exifTagTypes : {
25856         // byte, 8-bit unsigned int:
25857         1: {
25858             getValue: function (dataView, dataOffset) {
25859                 return dataView.getUint8(dataOffset);
25860             },
25861             size: 1
25862         },
25863         // ascii, 8-bit byte:
25864         2: {
25865             getValue: function (dataView, dataOffset) {
25866                 return String.fromCharCode(dataView.getUint8(dataOffset));
25867             },
25868             size: 1,
25869             ascii: true
25870         },
25871         // short, 16 bit int:
25872         3: {
25873             getValue: function (dataView, dataOffset, littleEndian) {
25874                 return dataView.getUint16(dataOffset, littleEndian);
25875             },
25876             size: 2
25877         },
25878         // long, 32 bit int:
25879         4: {
25880             getValue: function (dataView, dataOffset, littleEndian) {
25881                 return dataView.getUint32(dataOffset, littleEndian);
25882             },
25883             size: 4
25884         },
25885         // rational = two long values, first is numerator, second is denominator:
25886         5: {
25887             getValue: function (dataView, dataOffset, littleEndian) {
25888                 return dataView.getUint32(dataOffset, littleEndian) /
25889                     dataView.getUint32(dataOffset + 4, littleEndian);
25890             },
25891             size: 8
25892         },
25893         // slong, 32 bit signed int:
25894         9: {
25895             getValue: function (dataView, dataOffset, littleEndian) {
25896                 return dataView.getInt32(dataOffset, littleEndian);
25897             },
25898             size: 4
25899         },
25900         // srational, two slongs, first is numerator, second is denominator:
25901         10: {
25902             getValue: function (dataView, dataOffset, littleEndian) {
25903                 return dataView.getInt32(dataOffset, littleEndian) /
25904                     dataView.getInt32(dataOffset + 4, littleEndian);
25905             },
25906             size: 8
25907         }
25908     },
25909     
25910     footer : {
25911         STANDARD : [
25912             {
25913                 tag : 'div',
25914                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25915                 action : 'rotate-left',
25916                 cn : [
25917                     {
25918                         tag : 'button',
25919                         cls : 'btn btn-default',
25920                         html : '<i class="fa fa-undo"></i>'
25921                     }
25922                 ]
25923             },
25924             {
25925                 tag : 'div',
25926                 cls : 'btn-group roo-upload-cropbox-picture',
25927                 action : 'picture',
25928                 cn : [
25929                     {
25930                         tag : 'button',
25931                         cls : 'btn btn-default',
25932                         html : '<i class="fa fa-picture-o"></i>'
25933                     }
25934                 ]
25935             },
25936             {
25937                 tag : 'div',
25938                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25939                 action : 'rotate-right',
25940                 cn : [
25941                     {
25942                         tag : 'button',
25943                         cls : 'btn btn-default',
25944                         html : '<i class="fa fa-repeat"></i>'
25945                     }
25946                 ]
25947             }
25948         ],
25949         DOCUMENT : [
25950             {
25951                 tag : 'div',
25952                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25953                 action : 'rotate-left',
25954                 cn : [
25955                     {
25956                         tag : 'button',
25957                         cls : 'btn btn-default',
25958                         html : '<i class="fa fa-undo"></i>'
25959                     }
25960                 ]
25961             },
25962             {
25963                 tag : 'div',
25964                 cls : 'btn-group roo-upload-cropbox-download',
25965                 action : 'download',
25966                 cn : [
25967                     {
25968                         tag : 'button',
25969                         cls : 'btn btn-default',
25970                         html : '<i class="fa fa-download"></i>'
25971                     }
25972                 ]
25973             },
25974             {
25975                 tag : 'div',
25976                 cls : 'btn-group roo-upload-cropbox-crop',
25977                 action : 'crop',
25978                 cn : [
25979                     {
25980                         tag : 'button',
25981                         cls : 'btn btn-default',
25982                         html : '<i class="fa fa-crop"></i>'
25983                     }
25984                 ]
25985             },
25986             {
25987                 tag : 'div',
25988                 cls : 'btn-group roo-upload-cropbox-trash',
25989                 action : 'trash',
25990                 cn : [
25991                     {
25992                         tag : 'button',
25993                         cls : 'btn btn-default',
25994                         html : '<i class="fa fa-trash"></i>'
25995                     }
25996                 ]
25997             },
25998             {
25999                 tag : 'div',
26000                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26001                 action : 'rotate-right',
26002                 cn : [
26003                     {
26004                         tag : 'button',
26005                         cls : 'btn btn-default',
26006                         html : '<i class="fa fa-repeat"></i>'
26007                     }
26008                 ]
26009             }
26010         ],
26011         ROTATOR : [
26012             {
26013                 tag : 'div',
26014                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26015                 action : 'rotate-left',
26016                 cn : [
26017                     {
26018                         tag : 'button',
26019                         cls : 'btn btn-default',
26020                         html : '<i class="fa fa-undo"></i>'
26021                     }
26022                 ]
26023             },
26024             {
26025                 tag : 'div',
26026                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26027                 action : 'rotate-right',
26028                 cn : [
26029                     {
26030                         tag : 'button',
26031                         cls : 'btn btn-default',
26032                         html : '<i class="fa fa-repeat"></i>'
26033                     }
26034                 ]
26035             }
26036         ]
26037     }
26038 });
26039
26040 /*
26041 * Licence: LGPL
26042 */
26043
26044 /**
26045  * @class Roo.bootstrap.DocumentManager
26046  * @extends Roo.bootstrap.Component
26047  * Bootstrap DocumentManager class
26048  * @cfg {String} paramName default 'imageUpload'
26049  * @cfg {String} method default POST
26050  * @cfg {String} url action url
26051  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26052  * @cfg {Boolean} multiple multiple upload default true
26053  * @cfg {Number} thumbSize default 300
26054  * @cfg {String} fieldLabel
26055  * @cfg {Number} labelWidth default 4
26056  * @cfg {String} labelAlign (left|top) default left
26057  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26058  * 
26059  * @constructor
26060  * Create a new DocumentManager
26061  * @param {Object} config The config object
26062  */
26063
26064 Roo.bootstrap.DocumentManager = function(config){
26065     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26066     
26067     this.addEvents({
26068         /**
26069          * @event initial
26070          * Fire when initial the DocumentManager
26071          * @param {Roo.bootstrap.DocumentManager} this
26072          */
26073         "initial" : true,
26074         /**
26075          * @event inspect
26076          * inspect selected file
26077          * @param {Roo.bootstrap.DocumentManager} this
26078          * @param {File} file
26079          */
26080         "inspect" : true,
26081         /**
26082          * @event exception
26083          * Fire when xhr load exception
26084          * @param {Roo.bootstrap.DocumentManager} this
26085          * @param {XMLHttpRequest} xhr
26086          */
26087         "exception" : true,
26088         /**
26089          * @event prepare
26090          * prepare the form data
26091          * @param {Roo.bootstrap.DocumentManager} this
26092          * @param {Object} formData
26093          */
26094         "prepare" : true,
26095         /**
26096          * @event remove
26097          * Fire when remove the file
26098          * @param {Roo.bootstrap.DocumentManager} this
26099          * @param {Object} file
26100          */
26101         "remove" : true,
26102         /**
26103          * @event refresh
26104          * Fire after refresh the file
26105          * @param {Roo.bootstrap.DocumentManager} this
26106          */
26107         "refresh" : true,
26108         /**
26109          * @event click
26110          * Fire after click the image
26111          * @param {Roo.bootstrap.DocumentManager} this
26112          * @param {Object} file
26113          */
26114         "click" : true,
26115         /**
26116          * @event edit
26117          * Fire when upload a image and editable set to true
26118          * @param {Roo.bootstrap.DocumentManager} this
26119          * @param {Object} file
26120          */
26121         "edit" : true,
26122         /**
26123          * @event beforeselectfile
26124          * Fire before select file
26125          * @param {Roo.bootstrap.DocumentManager} this
26126          */
26127         "beforeselectfile" : true,
26128         /**
26129          * @event process
26130          * Fire before process file
26131          * @param {Roo.bootstrap.DocumentManager} this
26132          * @param {Object} file
26133          */
26134         "process" : true
26135         
26136     });
26137 };
26138
26139 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26140     
26141     boxes : 0,
26142     inputName : '',
26143     thumbSize : 300,
26144     multiple : true,
26145     files : [],
26146     method : 'POST',
26147     url : '',
26148     paramName : 'imageUpload',
26149     fieldLabel : '',
26150     labelWidth : 4,
26151     labelAlign : 'left',
26152     editable : true,
26153     delegates : [],
26154     
26155     
26156     xhr : false, 
26157     
26158     getAutoCreate : function()
26159     {   
26160         var managerWidget = {
26161             tag : 'div',
26162             cls : 'roo-document-manager',
26163             cn : [
26164                 {
26165                     tag : 'input',
26166                     cls : 'roo-document-manager-selector',
26167                     type : 'file'
26168                 },
26169                 {
26170                     tag : 'div',
26171                     cls : 'roo-document-manager-uploader',
26172                     cn : [
26173                         {
26174                             tag : 'div',
26175                             cls : 'roo-document-manager-upload-btn',
26176                             html : '<i class="fa fa-plus"></i>'
26177                         }
26178                     ]
26179                     
26180                 }
26181             ]
26182         };
26183         
26184         var content = [
26185             {
26186                 tag : 'div',
26187                 cls : 'column col-md-12',
26188                 cn : managerWidget
26189             }
26190         ];
26191         
26192         if(this.fieldLabel.length){
26193             
26194             content = [
26195                 {
26196                     tag : 'div',
26197                     cls : 'column col-md-12',
26198                     html : this.fieldLabel
26199                 },
26200                 {
26201                     tag : 'div',
26202                     cls : 'column col-md-12',
26203                     cn : managerWidget
26204                 }
26205             ];
26206
26207             if(this.labelAlign == 'left'){
26208                 content = [
26209                     {
26210                         tag : 'div',
26211                         cls : 'column col-md-' + this.labelWidth,
26212                         html : this.fieldLabel
26213                     },
26214                     {
26215                         tag : 'div',
26216                         cls : 'column col-md-' + (12 - this.labelWidth),
26217                         cn : managerWidget
26218                     }
26219                 ];
26220                 
26221             }
26222         }
26223         
26224         var cfg = {
26225             tag : 'div',
26226             cls : 'row clearfix',
26227             cn : content
26228         };
26229         
26230         return cfg;
26231         
26232     },
26233     
26234     initEvents : function()
26235     {
26236         this.managerEl = this.el.select('.roo-document-manager', true).first();
26237         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26238         
26239         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26240         this.selectorEl.hide();
26241         
26242         if(this.multiple){
26243             this.selectorEl.attr('multiple', 'multiple');
26244         }
26245         
26246         this.selectorEl.on('change', this.onFileSelected, this);
26247         
26248         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26249         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26250         
26251         this.uploader.on('click', this.onUploaderClick, this);
26252         
26253         this.renderProgressDialog();
26254         
26255         var _this = this;
26256         
26257         window.addEventListener("resize", function() { _this.refresh(); } );
26258         
26259         this.fireEvent('initial', this);
26260     },
26261     
26262     renderProgressDialog : function()
26263     {
26264         var _this = this;
26265         
26266         this.progressDialog = new Roo.bootstrap.Modal({
26267             cls : 'roo-document-manager-progress-dialog',
26268             allow_close : false,
26269             title : '',
26270             buttons : [
26271                 {
26272                     name  :'cancel',
26273                     weight : 'danger',
26274                     html : 'Cancel'
26275                 }
26276             ], 
26277             listeners : { 
26278                 btnclick : function() {
26279                     _this.uploadCancel();
26280                     this.hide();
26281                 }
26282             }
26283         });
26284          
26285         this.progressDialog.render(Roo.get(document.body));
26286          
26287         this.progress = new Roo.bootstrap.Progress({
26288             cls : 'roo-document-manager-progress',
26289             active : true,
26290             striped : true
26291         });
26292         
26293         this.progress.render(this.progressDialog.getChildContainer());
26294         
26295         this.progressBar = new Roo.bootstrap.ProgressBar({
26296             cls : 'roo-document-manager-progress-bar',
26297             aria_valuenow : 0,
26298             aria_valuemin : 0,
26299             aria_valuemax : 12,
26300             panel : 'success'
26301         });
26302         
26303         this.progressBar.render(this.progress.getChildContainer());
26304     },
26305     
26306     onUploaderClick : function(e)
26307     {
26308         e.preventDefault();
26309      
26310         if(this.fireEvent('beforeselectfile', this) != false){
26311             this.selectorEl.dom.click();
26312         }
26313         
26314     },
26315     
26316     onFileSelected : function(e)
26317     {
26318         e.preventDefault();
26319         
26320         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26321             return;
26322         }
26323         
26324         Roo.each(this.selectorEl.dom.files, function(file){
26325             if(this.fireEvent('inspect', this, file) != false){
26326                 this.files.push(file);
26327             }
26328         }, this);
26329         
26330         this.queue();
26331         
26332     },
26333     
26334     queue : function()
26335     {
26336         this.selectorEl.dom.value = '';
26337         
26338         if(!this.files.length){
26339             return;
26340         }
26341         
26342         if(this.boxes > 0 && this.files.length > this.boxes){
26343             this.files = this.files.slice(0, this.boxes);
26344         }
26345         
26346         this.uploader.show();
26347         
26348         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26349             this.uploader.hide();
26350         }
26351         
26352         var _this = this;
26353         
26354         var files = [];
26355         
26356         var docs = [];
26357         
26358         Roo.each(this.files, function(file){
26359             
26360             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26361                 var f = this.renderPreview(file);
26362                 files.push(f);
26363                 return;
26364             }
26365             
26366             if(file.type.indexOf('image') != -1){
26367                 this.delegates.push(
26368                     (function(){
26369                         _this.process(file);
26370                     }).createDelegate(this)
26371                 );
26372         
26373                 return;
26374             }
26375             
26376             docs.push(
26377                 (function(){
26378                     _this.process(file);
26379                 }).createDelegate(this)
26380             );
26381             
26382         }, this);
26383         
26384         this.files = files;
26385         
26386         this.delegates = this.delegates.concat(docs);
26387         
26388         if(!this.delegates.length){
26389             this.refresh();
26390             return;
26391         }
26392         
26393         this.progressBar.aria_valuemax = this.delegates.length;
26394         
26395         this.arrange();
26396         
26397         return;
26398     },
26399     
26400     arrange : function()
26401     {
26402         if(!this.delegates.length){
26403             this.progressDialog.hide();
26404             this.refresh();
26405             return;
26406         }
26407         
26408         var delegate = this.delegates.shift();
26409         
26410         this.progressDialog.show();
26411         
26412         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26413         
26414         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26415         
26416         delegate();
26417     },
26418     
26419     refresh : function()
26420     {
26421         this.uploader.show();
26422         
26423         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26424             this.uploader.hide();
26425         }
26426         
26427         Roo.isTouch ? this.closable(false) : this.closable(true);
26428         
26429         this.fireEvent('refresh', this);
26430     },
26431     
26432     onRemove : function(e, el, o)
26433     {
26434         e.preventDefault();
26435         
26436         this.fireEvent('remove', this, o);
26437         
26438     },
26439     
26440     remove : function(o)
26441     {
26442         var files = [];
26443         
26444         Roo.each(this.files, function(file){
26445             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26446                 files.push(file);
26447                 return;
26448             }
26449
26450             o.target.remove();
26451
26452         }, this);
26453         
26454         this.files = files;
26455         
26456         this.refresh();
26457     },
26458     
26459     clear : function()
26460     {
26461         Roo.each(this.files, function(file){
26462             if(!file.target){
26463                 return;
26464             }
26465             
26466             file.target.remove();
26467
26468         }, this);
26469         
26470         this.files = [];
26471         
26472         this.refresh();
26473     },
26474     
26475     onClick : function(e, el, o)
26476     {
26477         e.preventDefault();
26478         
26479         this.fireEvent('click', this, o);
26480         
26481     },
26482     
26483     closable : function(closable)
26484     {
26485         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26486             
26487             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26488             
26489             if(closable){
26490                 el.show();
26491                 return;
26492             }
26493             
26494             el.hide();
26495             
26496         }, this);
26497     },
26498     
26499     xhrOnLoad : function(xhr)
26500     {
26501         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26502             el.remove();
26503         }, this);
26504         
26505         if (xhr.readyState !== 4) {
26506             this.arrange();
26507             this.fireEvent('exception', this, xhr);
26508             return;
26509         }
26510
26511         var response = Roo.decode(xhr.responseText);
26512         
26513         if(!response.success){
26514             this.arrange();
26515             this.fireEvent('exception', this, xhr);
26516             return;
26517         }
26518         
26519         var file = this.renderPreview(response.data);
26520         
26521         this.files.push(file);
26522         
26523         this.arrange();
26524         
26525     },
26526     
26527     xhrOnError : function()
26528     {
26529         Roo.log('xhr on error');
26530         
26531         var response = Roo.decode(xhr.responseText);
26532           
26533         Roo.log(response);
26534         
26535         this.arrange();
26536     },
26537     
26538     process : function(file)
26539     {
26540         if(this.fireEvent('process', this, file) !== false){
26541             if(this.editable && file.type.indexOf('image') != -1){
26542                 this.fireEvent('edit', this, file);
26543                 return;
26544             }
26545
26546             this.uploadStart(file, false);
26547
26548             return;
26549         }
26550         
26551     },
26552     
26553     uploadStart : function(file, crop)
26554     {
26555         this.xhr = new XMLHttpRequest();
26556         
26557         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26558             this.arrange();
26559             return;
26560         }
26561         
26562         file.xhr = this.xhr;
26563             
26564         this.managerEl.createChild({
26565             tag : 'div',
26566             cls : 'roo-document-manager-loading',
26567             cn : [
26568                 {
26569                     tag : 'div',
26570                     tooltip : file.name,
26571                     cls : 'roo-document-manager-thumb',
26572                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26573                 }
26574             ]
26575
26576         });
26577
26578         this.xhr.open(this.method, this.url, true);
26579         
26580         var headers = {
26581             "Accept": "application/json",
26582             "Cache-Control": "no-cache",
26583             "X-Requested-With": "XMLHttpRequest"
26584         };
26585         
26586         for (var headerName in headers) {
26587             var headerValue = headers[headerName];
26588             if (headerValue) {
26589                 this.xhr.setRequestHeader(headerName, headerValue);
26590             }
26591         }
26592         
26593         var _this = this;
26594         
26595         this.xhr.onload = function()
26596         {
26597             _this.xhrOnLoad(_this.xhr);
26598         }
26599         
26600         this.xhr.onerror = function()
26601         {
26602             _this.xhrOnError(_this.xhr);
26603         }
26604         
26605         var formData = new FormData();
26606
26607         formData.append('returnHTML', 'NO');
26608         
26609         if(crop){
26610             formData.append('crop', crop);
26611         }
26612         
26613         formData.append(this.paramName, file, file.name);
26614         
26615         if(this.fireEvent('prepare', this, formData) != false){
26616             this.xhr.send(formData);
26617         };
26618     },
26619     
26620     uploadCancel : function()
26621     {
26622         if (this.xhr) {
26623             this.xhr.abort();
26624         }
26625         
26626         
26627         this.delegates = [];
26628         
26629         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26630             el.remove();
26631         }, this);
26632         
26633         this.arrange();
26634     },
26635     
26636     renderPreview : function(file)
26637     {
26638         if(typeof(file.target) != 'undefined' && file.target){
26639             return file;
26640         }
26641         
26642         var previewEl = this.managerEl.createChild({
26643             tag : 'div',
26644             cls : 'roo-document-manager-preview',
26645             cn : [
26646                 {
26647                     tag : 'div',
26648                     tooltip : file.filename,
26649                     cls : 'roo-document-manager-thumb',
26650                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26651                 },
26652                 {
26653                     tag : 'button',
26654                     cls : 'close',
26655                     html : '<i class="fa fa-times-circle"></i>'
26656                 }
26657             ]
26658         });
26659
26660         var close = previewEl.select('button.close', true).first();
26661
26662         close.on('click', this.onRemove, this, file);
26663
26664         file.target = previewEl;
26665
26666         var image = previewEl.select('img', true).first();
26667         
26668         var _this = this;
26669         
26670         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26671         
26672         image.on('click', this.onClick, this, file);
26673         
26674         return file;
26675         
26676     },
26677     
26678     onPreviewLoad : function(file, image)
26679     {
26680         if(typeof(file.target) == 'undefined' || !file.target){
26681             return;
26682         }
26683         
26684         var width = image.dom.naturalWidth || image.dom.width;
26685         var height = image.dom.naturalHeight || image.dom.height;
26686         
26687         if(width > height){
26688             file.target.addClass('wide');
26689             return;
26690         }
26691         
26692         file.target.addClass('tall');
26693         return;
26694         
26695     },
26696     
26697     uploadFromSource : function(file, crop)
26698     {
26699         this.xhr = new XMLHttpRequest();
26700         
26701         this.managerEl.createChild({
26702             tag : 'div',
26703             cls : 'roo-document-manager-loading',
26704             cn : [
26705                 {
26706                     tag : 'div',
26707                     tooltip : file.name,
26708                     cls : 'roo-document-manager-thumb',
26709                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26710                 }
26711             ]
26712
26713         });
26714
26715         this.xhr.open(this.method, this.url, true);
26716         
26717         var headers = {
26718             "Accept": "application/json",
26719             "Cache-Control": "no-cache",
26720             "X-Requested-With": "XMLHttpRequest"
26721         };
26722         
26723         for (var headerName in headers) {
26724             var headerValue = headers[headerName];
26725             if (headerValue) {
26726                 this.xhr.setRequestHeader(headerName, headerValue);
26727             }
26728         }
26729         
26730         var _this = this;
26731         
26732         this.xhr.onload = function()
26733         {
26734             _this.xhrOnLoad(_this.xhr);
26735         }
26736         
26737         this.xhr.onerror = function()
26738         {
26739             _this.xhrOnError(_this.xhr);
26740         }
26741         
26742         var formData = new FormData();
26743
26744         formData.append('returnHTML', 'NO');
26745         
26746         formData.append('crop', crop);
26747         
26748         if(typeof(file.filename) != 'undefined'){
26749             formData.append('filename', file.filename);
26750         }
26751         
26752         if(typeof(file.mimetype) != 'undefined'){
26753             formData.append('mimetype', file.mimetype);
26754         }
26755         
26756         if(this.fireEvent('prepare', this, formData) != false){
26757             this.xhr.send(formData);
26758         };
26759     }
26760 });
26761
26762 /*
26763 * Licence: LGPL
26764 */
26765
26766 /**
26767  * @class Roo.bootstrap.DocumentViewer
26768  * @extends Roo.bootstrap.Component
26769  * Bootstrap DocumentViewer class
26770  * 
26771  * @constructor
26772  * Create a new DocumentViewer
26773  * @param {Object} config The config object
26774  */
26775
26776 Roo.bootstrap.DocumentViewer = function(config){
26777     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26778     
26779     this.addEvents({
26780         /**
26781          * @event initial
26782          * Fire after initEvent
26783          * @param {Roo.bootstrap.DocumentViewer} this
26784          */
26785         "initial" : true,
26786         /**
26787          * @event click
26788          * Fire after click
26789          * @param {Roo.bootstrap.DocumentViewer} this
26790          */
26791         "click" : true,
26792         /**
26793          * @event trash
26794          * Fire after trash button
26795          * @param {Roo.bootstrap.DocumentViewer} this
26796          */
26797         "trash" : true
26798         
26799     });
26800 };
26801
26802 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26803     
26804     getAutoCreate : function()
26805     {
26806         var cfg = {
26807             tag : 'div',
26808             cls : 'roo-document-viewer',
26809             cn : [
26810                 {
26811                     tag : 'div',
26812                     cls : 'roo-document-viewer-body',
26813                     cn : [
26814                         {
26815                             tag : 'div',
26816                             cls : 'roo-document-viewer-thumb',
26817                             cn : [
26818                                 {
26819                                     tag : 'img',
26820                                     cls : 'roo-document-viewer-image'
26821                                 }
26822                             ]
26823                         }
26824                     ]
26825                 },
26826                 {
26827                     tag : 'div',
26828                     cls : 'roo-document-viewer-footer',
26829                     cn : {
26830                         tag : 'div',
26831                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26832                         cn : [
26833                             {
26834                                 tag : 'div',
26835                                 cls : 'btn-group',
26836                                 cn : [
26837                                     {
26838                                         tag : 'button',
26839                                         cls : 'btn btn-default roo-document-viewer-trash',
26840                                         html : '<i class="fa fa-trash"></i>'
26841                                     }
26842                                 ]
26843                             }
26844                         ]
26845                     }
26846                 }
26847             ]
26848         };
26849         
26850         return cfg;
26851     },
26852     
26853     initEvents : function()
26854     {
26855         
26856         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26857         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26858         
26859         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26860         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26861         
26862         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26863         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26864         
26865         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26866         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26867         
26868         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26869         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26870         
26871         this.bodyEl.on('click', this.onClick, this);
26872         
26873         this.trashBtn.on('click', this.onTrash, this);
26874         
26875     },
26876     
26877     initial : function()
26878     {
26879 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26880         
26881         
26882         this.fireEvent('initial', this);
26883         
26884     },
26885     
26886     onClick : function(e)
26887     {
26888         e.preventDefault();
26889         
26890         this.fireEvent('click', this);
26891     },
26892     
26893     onTrash : function(e)
26894     {
26895         e.preventDefault();
26896         
26897         this.fireEvent('trash', this);
26898     }
26899     
26900 });
26901 /*
26902  * - LGPL
26903  *
26904  * nav progress bar
26905  * 
26906  */
26907
26908 /**
26909  * @class Roo.bootstrap.NavProgressBar
26910  * @extends Roo.bootstrap.Component
26911  * Bootstrap NavProgressBar class
26912  * 
26913  * @constructor
26914  * Create a new nav progress bar
26915  * @param {Object} config The config object
26916  */
26917
26918 Roo.bootstrap.NavProgressBar = function(config){
26919     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26920
26921     this.bullets = this.bullets || [];
26922    
26923 //    Roo.bootstrap.NavProgressBar.register(this);
26924      this.addEvents({
26925         /**
26926              * @event changed
26927              * Fires when the active item changes
26928              * @param {Roo.bootstrap.NavProgressBar} this
26929              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26930              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
26931          */
26932         'changed': true
26933      });
26934     
26935 };
26936
26937 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
26938     
26939     bullets : [],
26940     barItems : [],
26941     
26942     getAutoCreate : function()
26943     {
26944         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26945         
26946         cfg = {
26947             tag : 'div',
26948             cls : 'roo-navigation-bar-group',
26949             cn : [
26950                 {
26951                     tag : 'div',
26952                     cls : 'roo-navigation-top-bar'
26953                 },
26954                 {
26955                     tag : 'div',
26956                     cls : 'roo-navigation-bullets-bar',
26957                     cn : [
26958                         {
26959                             tag : 'ul',
26960                             cls : 'roo-navigation-bar'
26961                         }
26962                     ]
26963                 },
26964                 
26965                 {
26966                     tag : 'div',
26967                     cls : 'roo-navigation-bottom-bar'
26968                 }
26969             ]
26970             
26971         };
26972         
26973         return cfg;
26974         
26975     },
26976     
26977     initEvents: function() 
26978     {
26979         
26980     },
26981     
26982     onRender : function(ct, position) 
26983     {
26984         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26985         
26986         if(this.bullets.length){
26987             Roo.each(this.bullets, function(b){
26988                this.addItem(b);
26989             }, this);
26990         }
26991         
26992         this.format();
26993         
26994     },
26995     
26996     addItem : function(cfg)
26997     {
26998         var item = new Roo.bootstrap.NavProgressItem(cfg);
26999         
27000         item.parentId = this.id;
27001         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27002         
27003         if(cfg.html){
27004             var top = new Roo.bootstrap.Element({
27005                 tag : 'div',
27006                 cls : 'roo-navigation-bar-text'
27007             });
27008             
27009             var bottom = new Roo.bootstrap.Element({
27010                 tag : 'div',
27011                 cls : 'roo-navigation-bar-text'
27012             });
27013             
27014             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27015             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27016             
27017             var topText = new Roo.bootstrap.Element({
27018                 tag : 'span',
27019                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27020             });
27021             
27022             var bottomText = new Roo.bootstrap.Element({
27023                 tag : 'span',
27024                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27025             });
27026             
27027             topText.onRender(top.el, null);
27028             bottomText.onRender(bottom.el, null);
27029             
27030             item.topEl = top;
27031             item.bottomEl = bottom;
27032         }
27033         
27034         this.barItems.push(item);
27035         
27036         return item;
27037     },
27038     
27039     getActive : function()
27040     {
27041         var active = false;
27042         
27043         Roo.each(this.barItems, function(v){
27044             
27045             if (!v.isActive()) {
27046                 return;
27047             }
27048             
27049             active = v;
27050             return false;
27051             
27052         });
27053         
27054         return active;
27055     },
27056     
27057     setActiveItem : function(item)
27058     {
27059         var prev = false;
27060         
27061         Roo.each(this.barItems, function(v){
27062             if (v.rid == item.rid) {
27063                 return ;
27064             }
27065             
27066             if (v.isActive()) {
27067                 v.setActive(false);
27068                 prev = v;
27069             }
27070         });
27071
27072         item.setActive(true);
27073         
27074         this.fireEvent('changed', this, item, prev);
27075     },
27076     
27077     getBarItem: function(rid)
27078     {
27079         var ret = false;
27080         
27081         Roo.each(this.barItems, function(e) {
27082             if (e.rid != rid) {
27083                 return;
27084             }
27085             
27086             ret =  e;
27087             return false;
27088         });
27089         
27090         return ret;
27091     },
27092     
27093     indexOfItem : function(item)
27094     {
27095         var index = false;
27096         
27097         Roo.each(this.barItems, function(v, i){
27098             
27099             if (v.rid != item.rid) {
27100                 return;
27101             }
27102             
27103             index = i;
27104             return false
27105         });
27106         
27107         return index;
27108     },
27109     
27110     setActiveNext : function()
27111     {
27112         var i = this.indexOfItem(this.getActive());
27113         
27114         if (i > this.barItems.length) {
27115             return;
27116         }
27117         
27118         this.setActiveItem(this.barItems[i+1]);
27119     },
27120     
27121     setActivePrev : function()
27122     {
27123         var i = this.indexOfItem(this.getActive());
27124         
27125         if (i  < 1) {
27126             return;
27127         }
27128         
27129         this.setActiveItem(this.barItems[i-1]);
27130     },
27131     
27132     format : function()
27133     {
27134         if(!this.barItems.length){
27135             return;
27136         }
27137      
27138         var width = 100 / this.barItems.length;
27139         
27140         Roo.each(this.barItems, function(i){
27141             i.el.setStyle('width', width + '%');
27142             i.topEl.el.setStyle('width', width + '%');
27143             i.bottomEl.el.setStyle('width', width + '%');
27144         }, this);
27145         
27146     }
27147     
27148 });
27149 /*
27150  * - LGPL
27151  *
27152  * Nav Progress Item
27153  * 
27154  */
27155
27156 /**
27157  * @class Roo.bootstrap.NavProgressItem
27158  * @extends Roo.bootstrap.Component
27159  * Bootstrap NavProgressItem class
27160  * @cfg {String} rid the reference id
27161  * @cfg {Boolean} active (true|false) Is item active default false
27162  * @cfg {Boolean} disabled (true|false) Is item active default false
27163  * @cfg {String} html
27164  * @cfg {String} position (top|bottom) text position default bottom
27165  * @cfg {String} icon show icon instead of number
27166  * 
27167  * @constructor
27168  * Create a new NavProgressItem
27169  * @param {Object} config The config object
27170  */
27171 Roo.bootstrap.NavProgressItem = function(config){
27172     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27173     this.addEvents({
27174         // raw events
27175         /**
27176          * @event click
27177          * The raw click event for the entire grid.
27178          * @param {Roo.bootstrap.NavProgressItem} this
27179          * @param {Roo.EventObject} e
27180          */
27181         "click" : true
27182     });
27183    
27184 };
27185
27186 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27187     
27188     rid : '',
27189     active : false,
27190     disabled : false,
27191     html : '',
27192     position : 'bottom',
27193     icon : false,
27194     
27195     getAutoCreate : function()
27196     {
27197         var iconCls = 'roo-navigation-bar-item-icon';
27198         
27199         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27200         
27201         var cfg = {
27202             tag: 'li',
27203             cls: 'roo-navigation-bar-item',
27204             cn : [
27205                 {
27206                     tag : 'i',
27207                     cls : iconCls
27208                 }
27209             ]
27210         };
27211         
27212         if(this.active){
27213             cfg.cls += ' active';
27214         }
27215         if(this.disabled){
27216             cfg.cls += ' disabled';
27217         }
27218         
27219         return cfg;
27220     },
27221     
27222     disable : function()
27223     {
27224         this.setDisabled(true);
27225     },
27226     
27227     enable : function()
27228     {
27229         this.setDisabled(false);
27230     },
27231     
27232     initEvents: function() 
27233     {
27234         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27235         
27236         this.iconEl.on('click', this.onClick, this);
27237     },
27238     
27239     onClick : function(e)
27240     {
27241         e.preventDefault();
27242         
27243         if(this.disabled){
27244             return;
27245         }
27246         
27247         if(this.fireEvent('click', this, e) === false){
27248             return;
27249         };
27250         
27251         this.parent().setActiveItem(this);
27252     },
27253     
27254     isActive: function () 
27255     {
27256         return this.active;
27257     },
27258     
27259     setActive : function(state)
27260     {
27261         if(this.active == state){
27262             return;
27263         }
27264         
27265         this.active = state;
27266         
27267         if (state) {
27268             this.el.addClass('active');
27269             return;
27270         }
27271         
27272         this.el.removeClass('active');
27273         
27274         return;
27275     },
27276     
27277     setDisabled : function(state)
27278     {
27279         if(this.disabled == state){
27280             return;
27281         }
27282         
27283         this.disabled = state;
27284         
27285         if (state) {
27286             this.el.addClass('disabled');
27287             return;
27288         }
27289         
27290         this.el.removeClass('disabled');
27291     },
27292     
27293     tooltipEl : function()
27294     {
27295         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27296     }
27297 });
27298  
27299
27300  /*
27301  * - LGPL
27302  *
27303  * FieldLabel
27304  * 
27305  */
27306
27307 /**
27308  * @class Roo.bootstrap.FieldLabel
27309  * @extends Roo.bootstrap.Component
27310  * Bootstrap FieldLabel class
27311  * @cfg {String} html contents of the element
27312  * @cfg {String} tag tag of the element default label
27313  * @cfg {String} cls class of the element
27314  * @cfg {String} target label target 
27315  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27316  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27317  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27318  * @cfg {String} iconTooltip default "This field is required"
27319  * 
27320  * @constructor
27321  * Create a new FieldLabel
27322  * @param {Object} config The config object
27323  */
27324
27325 Roo.bootstrap.FieldLabel = function(config){
27326     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27327     
27328     this.addEvents({
27329             /**
27330              * @event invalid
27331              * Fires after the field has been marked as invalid.
27332              * @param {Roo.form.FieldLabel} this
27333              * @param {String} msg The validation message
27334              */
27335             invalid : true,
27336             /**
27337              * @event valid
27338              * Fires after the field has been validated with no errors.
27339              * @param {Roo.form.FieldLabel} this
27340              */
27341             valid : true
27342         });
27343 };
27344
27345 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27346     
27347     tag: 'label',
27348     cls: '',
27349     html: '',
27350     target: '',
27351     allowBlank : true,
27352     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27353     validClass : 'text-success fa fa-lg fa-check',
27354     iconTooltip : 'This field is required',
27355     
27356     getAutoCreate : function(){
27357         
27358         var cfg = {
27359             tag : this.tag,
27360             cls : 'roo-bootstrap-field-label ' + this.cls,
27361             for : this.target,
27362             cn : [
27363                 {
27364                     tag : 'i',
27365                     cls : '',
27366                     tooltip : this.iconTooltip
27367                 },
27368                 {
27369                     tag : 'span',
27370                     html : this.html
27371                 }
27372             ] 
27373         };
27374         
27375         return cfg;
27376     },
27377     
27378     initEvents: function() 
27379     {
27380         Roo.bootstrap.Element.superclass.initEvents.call(this);
27381         
27382         this.iconEl = this.el.select('i', true).first();
27383         
27384         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27385         
27386         Roo.bootstrap.FieldLabel.register(this);
27387     },
27388     
27389     /**
27390      * Mark this field as valid
27391      */
27392     markValid : function()
27393     {
27394         this.iconEl.show();
27395         
27396         this.iconEl.removeClass(this.invalidClass);
27397         
27398         this.iconEl.addClass(this.validClass);
27399         
27400         this.fireEvent('valid', this);
27401     },
27402     
27403     /**
27404      * Mark this field as invalid
27405      * @param {String} msg The validation message
27406      */
27407     markInvalid : function(msg)
27408     {
27409         this.iconEl.show();
27410         
27411         this.iconEl.removeClass(this.validClass);
27412         
27413         this.iconEl.addClass(this.invalidClass);
27414         
27415         this.fireEvent('invalid', this, msg);
27416     }
27417     
27418    
27419 });
27420
27421 Roo.apply(Roo.bootstrap.FieldLabel, {
27422     
27423     groups: {},
27424     
27425      /**
27426     * register a FieldLabel Group
27427     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27428     */
27429     register : function(label)
27430     {
27431         if(this.groups.hasOwnProperty(label.target)){
27432             return;
27433         }
27434      
27435         this.groups[label.target] = label;
27436         
27437     },
27438     /**
27439     * fetch a FieldLabel Group based on the target
27440     * @param {string} target
27441     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27442     */
27443     get: function(target) {
27444         if (typeof(this.groups[target]) == 'undefined') {
27445             return false;
27446         }
27447         
27448         return this.groups[target] ;
27449     }
27450 });
27451
27452  
27453
27454