roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3951  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952  * @cfg {String} size (sm|lg|xl) default empty
3953  * @cfg {Number} max_width set the max width of modal
3954  * @cfg {Boolean} editableTitle can the title be edited
3955
3956  *
3957  *
3958  * @constructor
3959  * Create a new Modal Dialog
3960  * @param {Object} config The config object
3961  */
3962
3963 Roo.bootstrap.Modal = function(config){
3964     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3965     this.addEvents({
3966         // raw events
3967         /**
3968          * @event btnclick
3969          * The raw btnclick event for the button
3970          * @param {Roo.EventObject} e
3971          */
3972         "btnclick" : true,
3973         /**
3974          * @event resize
3975          * Fire when dialog resize
3976          * @param {Roo.bootstrap.Modal} this
3977          * @param {Roo.EventObject} e
3978          */
3979         "resize" : true,
3980         /**
3981          * @event titlechanged
3982          * Fire when the editable title has been changed
3983          * @param {Roo.bootstrap.Modal} this
3984          * @param {Roo.EventObject} value
3985          */
3986         "titlechanged" : true 
3987         
3988     });
3989     this.buttons = this.buttons || [];
3990
3991     if (this.tmpl) {
3992         this.tmpl = Roo.factory(this.tmpl);
3993     }
3994
3995 };
3996
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3998
3999     title : 'test dialog',
4000
4001     buttons : false,
4002
4003     // set on load...
4004
4005     html: false,
4006
4007     tmp: false,
4008
4009     specificTitle: false,
4010
4011     buttonPosition: 'right',
4012
4013     allow_close : true,
4014
4015     animate : true,
4016
4017     fitwindow: false,
4018     
4019      // private
4020     dialogEl: false,
4021     bodyEl:  false,
4022     footerEl:  false,
4023     titleEl:  false,
4024     closeEl:  false,
4025
4026     size: '',
4027     
4028     max_width: 0,
4029     
4030     max_height: 0,
4031     
4032     fit_content: false,
4033     editableTitle  : false,
4034
4035     onRender : function(ct, position)
4036     {
4037         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4038
4039         if(!this.el){
4040             var cfg = Roo.apply({},  this.getAutoCreate());
4041             cfg.id = Roo.id();
4042             //if(!cfg.name){
4043             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4044             //}
4045             //if (!cfg.name.length) {
4046             //    delete cfg.name;
4047            // }
4048             if (this.cls) {
4049                 cfg.cls += ' ' + this.cls;
4050             }
4051             if (this.style) {
4052                 cfg.style = this.style;
4053             }
4054             this.el = Roo.get(document.body).createChild(cfg, position);
4055         }
4056         //var type = this.el.dom.type;
4057
4058
4059         if(this.tabIndex !== undefined){
4060             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4061         }
4062
4063         this.dialogEl = this.el.select('.modal-dialog',true).first();
4064         this.bodyEl = this.el.select('.modal-body',true).first();
4065         this.closeEl = this.el.select('.modal-header .close', true).first();
4066         this.headerEl = this.el.select('.modal-header',true).first();
4067         this.titleEl = this.el.select('.modal-title',true).first();
4068         this.footerEl = this.el.select('.modal-footer',true).first();
4069
4070         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4071         
4072         //this.el.addClass("x-dlg-modal");
4073
4074         if (this.buttons.length) {
4075             Roo.each(this.buttons, function(bb) {
4076                 var b = Roo.apply({}, bb);
4077                 b.xns = b.xns || Roo.bootstrap;
4078                 b.xtype = b.xtype || 'Button';
4079                 if (typeof(b.listeners) == 'undefined') {
4080                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4081                 }
4082
4083                 var btn = Roo.factory(b);
4084
4085                 btn.render(this.getButtonContainer());
4086
4087             },this);
4088         }
4089         // render the children.
4090         var nitems = [];
4091
4092         if(typeof(this.items) != 'undefined'){
4093             var items = this.items;
4094             delete this.items;
4095
4096             for(var i =0;i < items.length;i++) {
4097                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4098             }
4099         }
4100
4101         this.items = nitems;
4102
4103         // where are these used - they used to be body/close/footer
4104
4105
4106         this.initEvents();
4107         //this.el.addClass([this.fieldClass, this.cls]);
4108
4109     },
4110
4111     getAutoCreate : function()
4112     {
4113         // we will default to modal-body-overflow - might need to remove or make optional later.
4114         var bdy = {
4115                 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''), 
4116                 html : this.html || ''
4117         };
4118
4119         var title = {
4120             tag: 'h4',
4121             cls : 'modal-title',
4122             html : this.title
4123         };
4124
4125         if(this.specificTitle){ // WTF is this?
4126             title = this.title;
4127         }
4128
4129         var header = [];
4130         if (this.allow_close && Roo.bootstrap.version == 3) {
4131             header.push({
4132                 tag: 'button',
4133                 cls : 'close',
4134                 html : '&times'
4135             });
4136         }
4137
4138         header.push(title);
4139
4140         if (this.editableTitle) {
4141             header.push({
4142                 cls: 'form-control roo-editable-title d-none',
4143                 tag: 'input',
4144                 type: 'text'
4145             });
4146         }
4147         
4148         if (this.allow_close && Roo.bootstrap.version == 4) {
4149             header.push({
4150                 tag: 'button',
4151                 cls : 'close',
4152                 html : '&times'
4153             });
4154         }
4155         
4156         var size = '';
4157
4158         if(this.size.length){
4159             size = 'modal-' + this.size;
4160         }
4161         
4162         var footer = Roo.bootstrap.version == 3 ?
4163             {
4164                 cls : 'modal-footer',
4165                 cn : [
4166                     {
4167                         tag: 'div',
4168                         cls: 'btn-' + this.buttonPosition
4169                     }
4170                 ]
4171
4172             } :
4173             {  // BS4 uses mr-auto on left buttons....
4174                 cls : 'modal-footer'
4175             };
4176
4177             
4178
4179         
4180         
4181         var modal = {
4182             cls: "modal",
4183              cn : [
4184                 {
4185                     cls: "modal-dialog " + size,
4186                     cn : [
4187                         {
4188                             cls : "modal-content",
4189                             cn : [
4190                                 {
4191                                     cls : 'modal-header',
4192                                     cn : header
4193                                 },
4194                                 bdy,
4195                                 footer
4196                             ]
4197
4198                         }
4199                     ]
4200
4201                 }
4202             ]
4203         };
4204
4205         if(this.animate){
4206             modal.cls += ' fade';
4207         }
4208
4209         return modal;
4210
4211     },
4212     getChildContainer : function() {
4213
4214          return this.bodyEl;
4215
4216     },
4217     getButtonContainer : function() {
4218         
4219          return Roo.bootstrap.version == 4 ?
4220             this.el.select('.modal-footer',true).first()
4221             : this.el.select('.modal-footer div',true).first();
4222
4223     },
4224     initEvents : function()
4225     {
4226         if (this.allow_close) {
4227             this.closeEl.on('click', this.hide, this);
4228         }
4229         Roo.EventManager.onWindowResize(this.resize, this, true);
4230         if (this.editableTitle) {
4231             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4232             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233             this.headerEditEl.on('keyup', function(e) {
4234                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235                         this.toggleHeaderInput(false)
4236                     }
4237                 }, this);
4238             this.headerEditEl.on('blur', function(e) {
4239                 this.toggleHeaderInput(false)
4240             },this);
4241         }
4242
4243     },
4244   
4245
4246     resize : function()
4247     {
4248         this.maskEl.setSize(
4249             Roo.lib.Dom.getViewWidth(true),
4250             Roo.lib.Dom.getViewHeight(true)
4251         );
4252         
4253         if (this.fitwindow) {
4254             
4255            
4256             this.setSize(
4257                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4259             );
4260             return;
4261         }
4262         
4263         if(this.max_width !== 0) {
4264             
4265             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4266             
4267             if(this.height) {
4268                 this.setSize(w, this.height);
4269                 return;
4270             }
4271             
4272             if(this.max_height) {
4273                 this.setSize(w,Math.min(
4274                     this.max_height,
4275                     Roo.lib.Dom.getViewportHeight(true) - 60
4276                 ));
4277                 
4278                 return;
4279             }
4280             
4281             if(!this.fit_content) {
4282                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4283                 return;
4284             }
4285             
4286             this.setSize(w, Math.min(
4287                 60 +
4288                 this.headerEl.getHeight() + 
4289                 this.footerEl.getHeight() + 
4290                 this.getChildHeight(this.bodyEl.dom.childNodes),
4291                 Roo.lib.Dom.getViewportHeight(true) - 60)
4292             );
4293         }
4294         
4295     },
4296
4297     setSize : function(w,h)
4298     {
4299         if (!w && !h) {
4300             return;
4301         }
4302         
4303         this.resizeTo(w,h);
4304     },
4305
4306     show : function() {
4307
4308         if (!this.rendered) {
4309             this.render();
4310         }
4311         this.toggleHeaderInput(false);
4312         //this.el.setStyle('display', 'block');
4313         this.el.removeClass('hideing');
4314         this.el.dom.style.display='block';
4315         
4316         Roo.get(document.body).addClass('modal-open');
4317  
4318         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4319             
4320             (function(){
4321                 this.el.addClass('show');
4322                 this.el.addClass('in');
4323             }).defer(50, this);
4324         }else{
4325             this.el.addClass('show');
4326             this.el.addClass('in');
4327         }
4328
4329         // not sure how we can show data in here..
4330         //if (this.tmpl) {
4331         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4332         //}
4333
4334         Roo.get(document.body).addClass("x-body-masked");
4335         
4336         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4337         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338         this.maskEl.dom.style.display = 'block';
4339         this.maskEl.addClass('show');
4340         
4341         
4342         this.resize();
4343         
4344         this.fireEvent('show', this);
4345
4346         // set zindex here - otherwise it appears to be ignored...
4347         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4348
4349         (function () {
4350             this.items.forEach( function(e) {
4351                 e.layout ? e.layout() : false;
4352
4353             });
4354         }).defer(100,this);
4355
4356     },
4357     hide : function()
4358     {
4359         if(this.fireEvent("beforehide", this) !== false){
4360             
4361             this.maskEl.removeClass('show');
4362             
4363             this.maskEl.dom.style.display = '';
4364             Roo.get(document.body).removeClass("x-body-masked");
4365             this.el.removeClass('in');
4366             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4367
4368             if(this.animate){ // why
4369                 this.el.addClass('hideing');
4370                 this.el.removeClass('show');
4371                 (function(){
4372                     if (!this.el.hasClass('hideing')) {
4373                         return; // it's been shown again...
4374                     }
4375                     
4376                     this.el.dom.style.display='';
4377
4378                     Roo.get(document.body).removeClass('modal-open');
4379                     this.el.removeClass('hideing');
4380                 }).defer(150,this);
4381                 
4382             }else{
4383                 this.el.removeClass('show');
4384                 this.el.dom.style.display='';
4385                 Roo.get(document.body).removeClass('modal-open');
4386
4387             }
4388             this.fireEvent('hide', this);
4389         }
4390     },
4391     isVisible : function()
4392     {
4393         
4394         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4395         
4396     },
4397
4398     addButton : function(str, cb)
4399     {
4400
4401
4402         var b = Roo.apply({}, { html : str } );
4403         b.xns = b.xns || Roo.bootstrap;
4404         b.xtype = b.xtype || 'Button';
4405         if (typeof(b.listeners) == 'undefined') {
4406             b.listeners = { click : cb.createDelegate(this)  };
4407         }
4408
4409         var btn = Roo.factory(b);
4410
4411         btn.render(this.getButtonContainer());
4412
4413         return btn;
4414
4415     },
4416
4417     setDefaultButton : function(btn)
4418     {
4419         //this.el.select('.modal-footer').()
4420     },
4421
4422     resizeTo: function(w,h)
4423     {
4424         this.dialogEl.setWidth(w);
4425         
4426         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4427
4428         this.bodyEl.setHeight(h - diff);
4429         
4430         this.fireEvent('resize', this);
4431     },
4432     
4433     setContentSize  : function(w, h)
4434     {
4435
4436     },
4437     onButtonClick: function(btn,e)
4438     {
4439         //Roo.log([a,b,c]);
4440         this.fireEvent('btnclick', btn.name, e);
4441     },
4442      /**
4443      * Set the title of the Dialog
4444      * @param {String} str new Title
4445      */
4446     setTitle: function(str) {
4447         this.titleEl.dom.innerHTML = str;
4448         this.title = str;
4449     },
4450     /**
4451      * Set the body of the Dialog
4452      * @param {String} str new Title
4453      */
4454     setBody: function(str) {
4455         this.bodyEl.dom.innerHTML = str;
4456     },
4457     /**
4458      * Set the body of the Dialog using the template
4459      * @param {Obj} data - apply this data to the template and replace the body contents.
4460      */
4461     applyBody: function(obj)
4462     {
4463         if (!this.tmpl) {
4464             Roo.log("Error - using apply Body without a template");
4465             //code
4466         }
4467         this.tmpl.overwrite(this.bodyEl, obj);
4468     },
4469     
4470     getChildHeight : function(child_nodes)
4471     {
4472         if(
4473             !child_nodes ||
4474             child_nodes.length == 0
4475         ) {
4476             return 0;
4477         }
4478         
4479         var child_height = 0;
4480         
4481         for(var i = 0; i < child_nodes.length; i++) {
4482             
4483             /*
4484             * for modal with tabs...
4485             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4486                 
4487                 var layout_childs = child_nodes[i].childNodes;
4488                 
4489                 for(var j = 0; j < layout_childs.length; j++) {
4490                     
4491                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4492                         
4493                         var layout_body_childs = layout_childs[j].childNodes;
4494                         
4495                         for(var k = 0; k < layout_body_childs.length; k++) {
4496                             
4497                             if(layout_body_childs[k].classList.contains('navbar')) {
4498                                 child_height += layout_body_childs[k].offsetHeight;
4499                                 continue;
4500                             }
4501                             
4502                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4503                                 
4504                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4505                                 
4506                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4507                                     
4508                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4510                                         continue;
4511                                     }
4512                                     
4513                                 }
4514                                 
4515                             }
4516                             
4517                         }
4518                     }
4519                 }
4520                 continue;
4521             }
4522             */
4523             
4524             child_height += child_nodes[i].offsetHeight;
4525             // Roo.log(child_nodes[i].offsetHeight);
4526         }
4527         
4528         return child_height;
4529     },
4530     toggleHeaderInput : function(is_edit)
4531     {
4532         if (!this.editableTitle) {
4533             return; // not editable.
4534         }
4535         if (is_edit && this.is_header_editing) {
4536             return; // already editing..
4537         }
4538         if (is_edit) {
4539     
4540             this.headerEditEl.dom.value = this.title;
4541             this.headerEditEl.removeClass('d-none');
4542             this.headerEditEl.dom.focus();
4543             this.titleEl.addClass('d-none');
4544             
4545             this.is_header_editing = true;
4546             return
4547         }
4548         // flip back to not editing.
4549         this.title = this.headerEditEl.dom.value;
4550         this.headerEditEl.addClass('d-none');
4551         this.titleEl.removeClass('d-none');
4552         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553         this.is_header_editing = false;
4554         this.fireEvent('titlechanged', this, this.title);
4555     
4556             
4557         
4558     }
4559
4560 });
4561
4562
4563 Roo.apply(Roo.bootstrap.Modal,  {
4564     /**
4565          * Button config that displays a single OK button
4566          * @type Object
4567          */
4568         OK :  [{
4569             name : 'ok',
4570             weight : 'primary',
4571             html : 'OK'
4572         }],
4573         /**
4574          * Button config that displays Yes and No buttons
4575          * @type Object
4576          */
4577         YESNO : [
4578             {
4579                 name  : 'no',
4580                 html : 'No'
4581             },
4582             {
4583                 name  :'yes',
4584                 weight : 'primary',
4585                 html : 'Yes'
4586             }
4587         ],
4588
4589         /**
4590          * Button config that displays OK and Cancel buttons
4591          * @type Object
4592          */
4593         OKCANCEL : [
4594             {
4595                name : 'cancel',
4596                 html : 'Cancel'
4597             },
4598             {
4599                 name : 'ok',
4600                 weight : 'primary',
4601                 html : 'OK'
4602             }
4603         ],
4604         /**
4605          * Button config that displays Yes, No and Cancel buttons
4606          * @type Object
4607          */
4608         YESNOCANCEL : [
4609             {
4610                 name : 'yes',
4611                 weight : 'primary',
4612                 html : 'Yes'
4613             },
4614             {
4615                 name : 'no',
4616                 html : 'No'
4617             },
4618             {
4619                 name : 'cancel',
4620                 html : 'Cancel'
4621             }
4622         ],
4623         
4624         zIndex : 10001
4625 });
4626
4627 /*
4628  * - LGPL
4629  *
4630  * messagebox - can be used as a replace
4631  * 
4632  */
4633 /**
4634  * @class Roo.MessageBox
4635  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4636  * Example usage:
4637  *<pre><code>
4638 // Basic alert:
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4640
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4643     if (btn == 'ok'){
4644         // process text value...
4645     }
4646 });
4647
4648 // Show a dialog using config options:
4649 Roo.Msg.show({
4650    title:'Save Changes?',
4651    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652    buttons: Roo.Msg.YESNOCANCEL,
4653    fn: processResult,
4654    animEl: 'elId'
4655 });
4656 </code></pre>
4657  * @singleton
4658  */
4659 Roo.bootstrap.MessageBox = function(){
4660     var dlg, opt, mask, waitTimer;
4661     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662     var buttons, activeTextEl, bwidth;
4663
4664     
4665     // private
4666     var handleButton = function(button){
4667         dlg.hide();
4668         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4669     };
4670
4671     // private
4672     var handleHide = function(){
4673         if(opt && opt.cls){
4674             dlg.el.removeClass(opt.cls);
4675         }
4676         //if(waitTimer){
4677         //    Roo.TaskMgr.stop(waitTimer);
4678         //    waitTimer = null;
4679         //}
4680     };
4681
4682     // private
4683     var updateButtons = function(b){
4684         var width = 0;
4685         if(!b){
4686             buttons["ok"].hide();
4687             buttons["cancel"].hide();
4688             buttons["yes"].hide();
4689             buttons["no"].hide();
4690             dlg.footerEl.hide();
4691             
4692             return width;
4693         }
4694         dlg.footerEl.show();
4695         for(var k in buttons){
4696             if(typeof buttons[k] != "function"){
4697                 if(b[k]){
4698                     buttons[k].show();
4699                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700                     width += buttons[k].el.getWidth()+15;
4701                 }else{
4702                     buttons[k].hide();
4703                 }
4704             }
4705         }
4706         return width;
4707     };
4708
4709     // private
4710     var handleEsc = function(d, k, e){
4711         if(opt && opt.closable !== false){
4712             dlg.hide();
4713         }
4714         if(e){
4715             e.stopEvent();
4716         }
4717     };
4718
4719     return {
4720         /**
4721          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722          * @return {Roo.BasicDialog} The BasicDialog element
4723          */
4724         getDialog : function(){
4725            if(!dlg){
4726                 dlg = new Roo.bootstrap.Modal( {
4727                     //draggable: true,
4728                     //resizable:false,
4729                     //constraintoviewport:false,
4730                     //fixedcenter:true,
4731                     //collapsible : false,
4732                     //shim:true,
4733                     //modal: true,
4734                 //    width: 'auto',
4735                   //  height:100,
4736                     //buttonAlign:"center",
4737                     closeClick : function(){
4738                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4739                             handleButton("no");
4740                         }else{
4741                             handleButton("cancel");
4742                         }
4743                     }
4744                 });
4745                 dlg.render();
4746                 dlg.on("hide", handleHide);
4747                 mask = dlg.mask;
4748                 //dlg.addKeyListener(27, handleEsc);
4749                 buttons = {};
4750                 this.buttons = buttons;
4751                 var bt = this.buttonText;
4752                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4756                 //Roo.log(buttons);
4757                 bodyEl = dlg.bodyEl.createChild({
4758
4759                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760                         '<textarea class="roo-mb-textarea"></textarea>' +
4761                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4762                 });
4763                 msgEl = bodyEl.dom.firstChild;
4764                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765                 textboxEl.enableDisplayMode();
4766                 textboxEl.addKeyListener([10,13], function(){
4767                     if(dlg.isVisible() && opt && opt.buttons){
4768                         if(opt.buttons.ok){
4769                             handleButton("ok");
4770                         }else if(opt.buttons.yes){
4771                             handleButton("yes");
4772                         }
4773                     }
4774                 });
4775                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776                 textareaEl.enableDisplayMode();
4777                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778                 progressEl.enableDisplayMode();
4779                 
4780                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781                 var pf = progressEl.dom.firstChild;
4782                 if (pf) {
4783                     pp = Roo.get(pf.firstChild);
4784                     pp.setHeight(pf.offsetHeight);
4785                 }
4786                 
4787             }
4788             return dlg;
4789         },
4790
4791         /**
4792          * Updates the message box body text
4793          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794          * the XHTML-compliant non-breaking space character '&amp;#160;')
4795          * @return {Roo.MessageBox} This message box
4796          */
4797         updateText : function(text)
4798         {
4799             if(!dlg.isVisible() && !opt.width){
4800                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4802             }
4803             msgEl.innerHTML = text || '&#160;';
4804       
4805             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4807             var w = Math.max(
4808                     Math.min(opt.width || cw , this.maxWidth), 
4809                     Math.max(opt.minWidth || this.minWidth, bwidth)
4810             );
4811             if(opt.prompt){
4812                 activeTextEl.setWidth(w);
4813             }
4814             if(dlg.isVisible()){
4815                 dlg.fixedcenter = false;
4816             }
4817             // to big, make it scroll. = But as usual stupid IE does not support
4818             // !important..
4819             
4820             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4823             } else {
4824                 bodyEl.dom.style.height = '';
4825                 bodyEl.dom.style.overflowY = '';
4826             }
4827             if (cw > w) {
4828                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4829             } else {
4830                 bodyEl.dom.style.overflowX = '';
4831             }
4832             
4833             dlg.setContentSize(w, bodyEl.getHeight());
4834             if(dlg.isVisible()){
4835                 dlg.fixedcenter = true;
4836             }
4837             return this;
4838         },
4839
4840         /**
4841          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4842          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845          * @return {Roo.MessageBox} This message box
4846          */
4847         updateProgress : function(value, text){
4848             if(text){
4849                 this.updateText(text);
4850             }
4851             
4852             if (pp) { // weird bug on my firefox - for some reason this is not defined
4853                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855             }
4856             return this;
4857         },        
4858
4859         /**
4860          * Returns true if the message box is currently displayed
4861          * @return {Boolean} True if the message box is visible, else false
4862          */
4863         isVisible : function(){
4864             return dlg && dlg.isVisible();  
4865         },
4866
4867         /**
4868          * Hides the message box if it is displayed
4869          */
4870         hide : function(){
4871             if(this.isVisible()){
4872                 dlg.hide();
4873             }  
4874         },
4875
4876         /**
4877          * Displays a new message box, or reinitializes an existing message box, based on the config options
4878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879          * The following config object properties are supported:
4880          * <pre>
4881 Property    Type             Description
4882 ----------  ---------------  ------------------------------------------------------------------------------------
4883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4884                                    closes (defaults to undefined)
4885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4888                                    progress and wait dialogs will ignore this property and always hide the
4889                                    close button as they can only be closed programmatically.
4890 cls               String           A custom CSS class to apply to the message box element
4891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4892                                    displayed (defaults to 75)
4893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4894                                    function will be btn (the name of the button that was clicked, if applicable,
4895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4896                                    Progress and wait dialogs will ignore this option since they do not respond to
4897                                    user actions and can only be closed programmatically, so any required function
4898                                    should be called by the same code after it closes the dialog.
4899 icon              String           A CSS class that provides a background image to be used as an icon for
4900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4903 modal             Boolean          False to allow user interaction with the page while the message box is
4904                                    displayed (defaults to true)
4905 msg               String           A string that will replace the existing message box body text (defaults
4906                                    to the XHTML-compliant non-breaking space character '&#160;')
4907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4908 progress          Boolean          True to display a progress bar (defaults to false)
4909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4912 title             String           The title text
4913 value             String           The string value to set into the active textbox element if displayed
4914 wait              Boolean          True to display a progress bar (defaults to false)
4915 width             Number           The width of the dialog in pixels
4916 </pre>
4917          *
4918          * Example usage:
4919          * <pre><code>
4920 Roo.Msg.show({
4921    title: 'Address',
4922    msg: 'Please enter your address:',
4923    width: 300,
4924    buttons: Roo.MessageBox.OKCANCEL,
4925    multiline: true,
4926    fn: saveAddress,
4927    animEl: 'addAddressBtn'
4928 });
4929 </code></pre>
4930          * @param {Object} config Configuration options
4931          * @return {Roo.MessageBox} This message box
4932          */
4933         show : function(options)
4934         {
4935             
4936             // this causes nightmares if you show one dialog after another
4937             // especially on callbacks..
4938              
4939             if(this.isVisible()){
4940                 
4941                 this.hide();
4942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4944                 Roo.log("New Dialog Message:" +  options.msg )
4945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4947                 
4948             }
4949             var d = this.getDialog();
4950             opt = options;
4951             d.setTitle(opt.title || "&#160;");
4952             d.closeEl.setDisplayed(opt.closable !== false);
4953             activeTextEl = textboxEl;
4954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4955             if(opt.prompt){
4956                 if(opt.multiline){
4957                     textboxEl.hide();
4958                     textareaEl.show();
4959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4960                         opt.multiline : this.defaultTextHeight);
4961                     activeTextEl = textareaEl;
4962                 }else{
4963                     textboxEl.show();
4964                     textareaEl.hide();
4965                 }
4966             }else{
4967                 textboxEl.hide();
4968                 textareaEl.hide();
4969             }
4970             progressEl.setDisplayed(opt.progress === true);
4971             if (opt.progress) {
4972                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4973             }
4974             this.updateProgress(0);
4975             activeTextEl.dom.value = opt.value || "";
4976             if(opt.prompt){
4977                 dlg.setDefaultButton(activeTextEl);
4978             }else{
4979                 var bs = opt.buttons;
4980                 var db = null;
4981                 if(bs && bs.ok){
4982                     db = buttons["ok"];
4983                 }else if(bs && bs.yes){
4984                     db = buttons["yes"];
4985                 }
4986                 dlg.setDefaultButton(db);
4987             }
4988             bwidth = updateButtons(opt.buttons);
4989             this.updateText(opt.msg);
4990             if(opt.cls){
4991                 d.el.addClass(opt.cls);
4992             }
4993             d.proxyDrag = opt.proxyDrag === true;
4994             d.modal = opt.modal !== false;
4995             d.mask = opt.modal !== false ? mask : false;
4996             if(!d.isVisible()){
4997                 // force it to the end of the z-index stack so it gets a cursor in FF
4998                 document.body.appendChild(dlg.el.dom);
4999                 d.animateTarget = null;
5000                 d.show(options.animEl);
5001             }
5002             return this;
5003         },
5004
5005         /**
5006          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5007          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008          * and closing the message box when the process is complete.
5009          * @param {String} title The title bar text
5010          * @param {String} msg The message box body text
5011          * @return {Roo.MessageBox} This message box
5012          */
5013         progress : function(title, msg){
5014             this.show({
5015                 title : title,
5016                 msg : msg,
5017                 buttons: false,
5018                 progress:true,
5019                 closable:false,
5020                 minWidth: this.minProgressWidth,
5021                 modal : true
5022             });
5023             return this;
5024         },
5025
5026         /**
5027          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028          * If a callback function is passed it will be called after the user clicks the button, and the
5029          * id of the button that was clicked will be passed as the only parameter to the callback
5030          * (could also be the top-right close button).
5031          * @param {String} title The title bar text
5032          * @param {String} msg The message box body text
5033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034          * @param {Object} scope (optional) The scope of the callback function
5035          * @return {Roo.MessageBox} This message box
5036          */
5037         alert : function(title, msg, fn, scope)
5038         {
5039             this.show({
5040                 title : title,
5041                 msg : msg,
5042                 buttons: this.OK,
5043                 fn: fn,
5044                 closable : false,
5045                 scope : scope,
5046                 modal : true
5047             });
5048             return this;
5049         },
5050
5051         /**
5052          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5053          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054          * You are responsible for closing the message box when the process is complete.
5055          * @param {String} msg The message box body text
5056          * @param {String} title (optional) The title bar text
5057          * @return {Roo.MessageBox} This message box
5058          */
5059         wait : function(msg, title){
5060             this.show({
5061                 title : title,
5062                 msg : msg,
5063                 buttons: false,
5064                 closable:false,
5065                 progress:true,
5066                 modal:true,
5067                 width:300,
5068                 wait:true
5069             });
5070             waitTimer = Roo.TaskMgr.start({
5071                 run: function(i){
5072                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5073                 },
5074                 interval: 1000
5075             });
5076             return this;
5077         },
5078
5079         /**
5080          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083          * @param {String} title The title bar text
5084          * @param {String} msg The message box body text
5085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086          * @param {Object} scope (optional) The scope of the callback function
5087          * @return {Roo.MessageBox} This message box
5088          */
5089         confirm : function(title, msg, fn, scope){
5090             this.show({
5091                 title : title,
5092                 msg : msg,
5093                 buttons: this.YESNO,
5094                 fn: fn,
5095                 scope : scope,
5096                 modal : true
5097             });
5098             return this;
5099         },
5100
5101         /**
5102          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5104          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105          * (could also be the top-right close button) and the text that was entered will be passed as the two
5106          * parameters to the callback.
5107          * @param {String} title The title bar text
5108          * @param {String} msg The message box body text
5109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110          * @param {Object} scope (optional) The scope of the callback function
5111          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         prompt : function(title, msg, fn, scope, multiline){
5116             this.show({
5117                 title : title,
5118                 msg : msg,
5119                 buttons: this.OKCANCEL,
5120                 fn: fn,
5121                 minWidth:250,
5122                 scope : scope,
5123                 prompt:true,
5124                 multiline: multiline,
5125                 modal : true
5126             });
5127             return this;
5128         },
5129
5130         /**
5131          * Button config that displays a single OK button
5132          * @type Object
5133          */
5134         OK : {ok:true},
5135         /**
5136          * Button config that displays Yes and No buttons
5137          * @type Object
5138          */
5139         YESNO : {yes:true, no:true},
5140         /**
5141          * Button config that displays OK and Cancel buttons
5142          * @type Object
5143          */
5144         OKCANCEL : {ok:true, cancel:true},
5145         /**
5146          * Button config that displays Yes, No and Cancel buttons
5147          * @type Object
5148          */
5149         YESNOCANCEL : {yes:true, no:true, cancel:true},
5150
5151         /**
5152          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5153          * @type Number
5154          */
5155         defaultTextHeight : 75,
5156         /**
5157          * The maximum width in pixels of the message box (defaults to 600)
5158          * @type Number
5159          */
5160         maxWidth : 600,
5161         /**
5162          * The minimum width in pixels of the message box (defaults to 100)
5163          * @type Number
5164          */
5165         minWidth : 100,
5166         /**
5167          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5168          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5169          * @type Number
5170          */
5171         minProgressWidth : 250,
5172         /**
5173          * An object containing the default button text strings that can be overriden for localized language support.
5174          * Supported properties are: ok, cancel, yes and no.
5175          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5176          * @type Object
5177          */
5178         buttonText : {
5179             ok : "OK",
5180             cancel : "Cancel",
5181             yes : "Yes",
5182             no : "No"
5183         }
5184     };
5185 }();
5186
5187 /**
5188  * Shorthand for {@link Roo.MessageBox}
5189  */
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5192 /*
5193  * - LGPL
5194  *
5195  * navbar
5196  * 
5197  */
5198
5199 /**
5200  * @class Roo.bootstrap.Navbar
5201  * @extends Roo.bootstrap.Component
5202  * Bootstrap Navbar class
5203
5204  * @constructor
5205  * Create a new Navbar
5206  * @param {Object} config The config object
5207  */
5208
5209
5210 Roo.bootstrap.Navbar = function(config){
5211     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5212     this.addEvents({
5213         // raw events
5214         /**
5215          * @event beforetoggle
5216          * Fire before toggle the menu
5217          * @param {Roo.EventObject} e
5218          */
5219         "beforetoggle" : true
5220     });
5221 };
5222
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5224     
5225     
5226    
5227     // private
5228     navItems : false,
5229     loadMask : false,
5230     
5231     
5232     getAutoCreate : function(){
5233         
5234         
5235         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5236         
5237     },
5238     
5239     initEvents :function ()
5240     {
5241         //Roo.log(this.el.select('.navbar-toggle',true));
5242         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5243         
5244         var mark = {
5245             tag: "div",
5246             cls:"x-dlg-mask"
5247         };
5248         
5249         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5250         
5251         var size = this.el.getSize();
5252         this.maskEl.setSize(size.width, size.height);
5253         this.maskEl.enableDisplayMode("block");
5254         this.maskEl.hide();
5255         
5256         if(this.loadMask){
5257             this.maskEl.show();
5258         }
5259     },
5260     
5261     
5262     getChildContainer : function()
5263     {
5264         if (this.el && this.el.select('.collapse').getCount()) {
5265             return this.el.select('.collapse',true).first();
5266         }
5267         
5268         return this.el;
5269     },
5270     
5271     mask : function()
5272     {
5273         this.maskEl.show();
5274     },
5275     
5276     unmask : function()
5277     {
5278         this.maskEl.hide();
5279     },
5280     onToggle : function()
5281     {
5282         
5283         if(this.fireEvent('beforetoggle', this) === false){
5284             return;
5285         }
5286         var ce = this.el.select('.navbar-collapse',true).first();
5287       
5288         if (!ce.hasClass('show')) {
5289            this.expand();
5290         } else {
5291             this.collapse();
5292         }
5293         
5294         
5295     
5296     },
5297     /**
5298      * Expand the navbar pulldown 
5299      */
5300     expand : function ()
5301     {
5302        
5303         var ce = this.el.select('.navbar-collapse',true).first();
5304         if (ce.hasClass('collapsing')) {
5305             return;
5306         }
5307         ce.dom.style.height = '';
5308                // show it...
5309         ce.addClass('in'); // old...
5310         ce.removeClass('collapse');
5311         ce.addClass('show');
5312         var h = ce.getHeight();
5313         Roo.log(h);
5314         ce.removeClass('show');
5315         // at this point we should be able to see it..
5316         ce.addClass('collapsing');
5317         
5318         ce.setHeight(0); // resize it ...
5319         ce.on('transitionend', function() {
5320             //Roo.log('done transition');
5321             ce.removeClass('collapsing');
5322             ce.addClass('show');
5323             ce.removeClass('collapse');
5324
5325             ce.dom.style.height = '';
5326         }, this, { single: true} );
5327         ce.setHeight(h);
5328         ce.dom.scrollTop = 0;
5329     },
5330     /**
5331      * Collapse the navbar pulldown 
5332      */
5333     collapse : function()
5334     {
5335          var ce = this.el.select('.navbar-collapse',true).first();
5336        
5337         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338             // it's collapsed or collapsing..
5339             return;
5340         }
5341         ce.removeClass('in'); // old...
5342         ce.setHeight(ce.getHeight());
5343         ce.removeClass('show');
5344         ce.addClass('collapsing');
5345         
5346         ce.on('transitionend', function() {
5347             ce.dom.style.height = '';
5348             ce.removeClass('collapsing');
5349             ce.addClass('collapse');
5350         }, this, { single: true} );
5351         ce.setHeight(0);
5352     }
5353     
5354     
5355     
5356 });
5357
5358
5359
5360  
5361
5362  /*
5363  * - LGPL
5364  *
5365  * navbar
5366  * 
5367  */
5368
5369 /**
5370  * @class Roo.bootstrap.NavSimplebar
5371  * @extends Roo.bootstrap.Navbar
5372  * Bootstrap Sidebar class
5373  *
5374  * @cfg {Boolean} inverse is inverted color
5375  * 
5376  * @cfg {String} type (nav | pills | tabs)
5377  * @cfg {Boolean} arrangement stacked | justified
5378  * @cfg {String} align (left | right) alignment
5379  * 
5380  * @cfg {Boolean} main (true|false) main nav bar? default false
5381  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5382  * 
5383  * @cfg {String} tag (header|footer|nav|div) default is nav 
5384
5385  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5386  * 
5387  * 
5388  * @constructor
5389  * Create a new Sidebar
5390  * @param {Object} config The config object
5391  */
5392
5393
5394 Roo.bootstrap.NavSimplebar = function(config){
5395     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5396 };
5397
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5399     
5400     inverse: false,
5401     
5402     type: false,
5403     arrangement: '',
5404     align : false,
5405     
5406     weight : 'light',
5407     
5408     main : false,
5409     
5410     
5411     tag : false,
5412     
5413     
5414     getAutoCreate : function(){
5415         
5416         
5417         var cfg = {
5418             tag : this.tag || 'div',
5419             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5420         };
5421         if (['light','white'].indexOf(this.weight) > -1) {
5422             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5423         }
5424         cfg.cls += ' bg-' + this.weight;
5425         
5426         if (this.inverse) {
5427             cfg.cls += ' navbar-inverse';
5428             
5429         }
5430         
5431         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5432         
5433         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5434             return cfg;
5435         }
5436         
5437         
5438     
5439         
5440         cfg.cn = [
5441             {
5442                 cls: 'nav nav-' + this.xtype,
5443                 tag : 'ul'
5444             }
5445         ];
5446         
5447          
5448         this.type = this.type || 'nav';
5449         if (['tabs','pills'].indexOf(this.type) != -1) {
5450             cfg.cn[0].cls += ' nav-' + this.type
5451         
5452         
5453         } else {
5454             if (this.type!=='nav') {
5455                 Roo.log('nav type must be nav/tabs/pills')
5456             }
5457             cfg.cn[0].cls += ' navbar-nav'
5458         }
5459         
5460         
5461         
5462         
5463         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464             cfg.cn[0].cls += ' nav-' + this.arrangement;
5465         }
5466         
5467         
5468         if (this.align === 'right') {
5469             cfg.cn[0].cls += ' navbar-right';
5470         }
5471         
5472         
5473         
5474         
5475         return cfg;
5476     
5477         
5478     }
5479     
5480     
5481     
5482 });
5483
5484
5485
5486  
5487
5488  
5489        /*
5490  * - LGPL
5491  *
5492  * navbar
5493  * navbar-fixed-top
5494  * navbar-expand-md  fixed-top 
5495  */
5496
5497 /**
5498  * @class Roo.bootstrap.NavHeaderbar
5499  * @extends Roo.bootstrap.NavSimplebar
5500  * Bootstrap Sidebar class
5501  *
5502  * @cfg {String} brand what is brand
5503  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504  * @cfg {String} brand_href href of the brand
5505  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5506  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5509  * 
5510  * @constructor
5511  * Create a new Sidebar
5512  * @param {Object} config The config object
5513  */
5514
5515
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5518       
5519 };
5520
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5522     
5523     position: '',
5524     brand: '',
5525     brand_href: false,
5526     srButton : true,
5527     autohide : false,
5528     desktopCenter : false,
5529    
5530     
5531     getAutoCreate : function(){
5532         
5533         var   cfg = {
5534             tag: this.nav || 'nav',
5535             cls: 'navbar navbar-expand-md',
5536             role: 'navigation',
5537             cn: []
5538         };
5539         
5540         var cn = cfg.cn;
5541         if (this.desktopCenter) {
5542             cn.push({cls : 'container', cn : []});
5543             cn = cn[0].cn;
5544         }
5545         
5546         if(this.srButton){
5547             var btn = {
5548                 tag: 'button',
5549                 type: 'button',
5550                 cls: 'navbar-toggle navbar-toggler',
5551                 'data-toggle': 'collapse',
5552                 cn: [
5553                     {
5554                         tag: 'span',
5555                         cls: 'sr-only',
5556                         html: 'Toggle navigation'
5557                     },
5558                     {
5559                         tag: 'span',
5560                         cls: 'icon-bar navbar-toggler-icon'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     }
5570                 ]
5571             };
5572             
5573             cn.push( Roo.bootstrap.version == 4 ? btn : {
5574                 tag: 'div',
5575                 cls: 'navbar-header',
5576                 cn: [
5577                     btn
5578                 ]
5579             });
5580         }
5581         
5582         cn.push({
5583             tag: 'div',
5584             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5585             cn : []
5586         });
5587         
5588         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5589         
5590         if (['light','white'].indexOf(this.weight) > -1) {
5591             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5592         }
5593         cfg.cls += ' bg-' + this.weight;
5594         
5595         
5596         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5598             
5599             // tag can override this..
5600             
5601             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5602         }
5603         
5604         if (this.brand !== '') {
5605             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5607                 tag: 'a',
5608                 href: this.brand_href ? this.brand_href : '#',
5609                 cls: 'navbar-brand',
5610                 cn: [
5611                 this.brand
5612                 ]
5613             });
5614         }
5615         
5616         if(this.main){
5617             cfg.cls += ' main-nav';
5618         }
5619         
5620         
5621         return cfg;
5622
5623         
5624     },
5625     getHeaderChildContainer : function()
5626     {
5627         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628             return this.el.select('.navbar-header',true).first();
5629         }
5630         
5631         return this.getChildContainer();
5632     },
5633     
5634     getChildContainer : function()
5635     {
5636          
5637         return this.el.select('.roo-navbar-collapse',true).first();
5638          
5639         
5640     },
5641     
5642     initEvents : function()
5643     {
5644         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5645         
5646         if (this.autohide) {
5647             
5648             var prevScroll = 0;
5649             var ft = this.el;
5650             
5651             Roo.get(document).on('scroll',function(e) {
5652                 var ns = Roo.get(document).getScroll().top;
5653                 var os = prevScroll;
5654                 prevScroll = ns;
5655                 
5656                 if(ns > os){
5657                     ft.removeClass('slideDown');
5658                     ft.addClass('slideUp');
5659                     return;
5660                 }
5661                 ft.removeClass('slideUp');
5662                 ft.addClass('slideDown');
5663                  
5664               
5665           },this);
5666         }
5667     }    
5668     
5669 });
5670
5671
5672
5673  
5674
5675  /*
5676  * - LGPL
5677  *
5678  * navbar
5679  * 
5680  */
5681
5682 /**
5683  * @class Roo.bootstrap.NavSidebar
5684  * @extends Roo.bootstrap.Navbar
5685  * Bootstrap Sidebar class
5686  * 
5687  * @constructor
5688  * Create a new Sidebar
5689  * @param {Object} config The config object
5690  */
5691
5692
5693 Roo.bootstrap.NavSidebar = function(config){
5694     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5695 };
5696
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5698     
5699     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5700     
5701     getAutoCreate : function(){
5702         
5703         
5704         return  {
5705             tag: 'div',
5706             cls: 'sidebar sidebar-nav'
5707         };
5708     
5709         
5710     }
5711     
5712     
5713     
5714 });
5715
5716
5717
5718  
5719
5720  /*
5721  * - LGPL
5722  *
5723  * nav group
5724  * 
5725  */
5726
5727 /**
5728  * @class Roo.bootstrap.NavGroup
5729  * @extends Roo.bootstrap.Component
5730  * Bootstrap NavGroup class
5731  * @cfg {String} align (left|right)
5732  * @cfg {Boolean} inverse
5733  * @cfg {String} type (nav|pills|tab) default nav
5734  * @cfg {String} navId - reference Id for navbar.
5735
5736  * 
5737  * @constructor
5738  * Create a new nav group
5739  * @param {Object} config The config object
5740  */
5741
5742 Roo.bootstrap.NavGroup = function(config){
5743     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5744     this.navItems = [];
5745    
5746     Roo.bootstrap.NavGroup.register(this);
5747      this.addEvents({
5748         /**
5749              * @event changed
5750              * Fires when the active item changes
5751              * @param {Roo.bootstrap.NavGroup} this
5752              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5754          */
5755         'changed': true
5756      });
5757     
5758 };
5759
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5761     
5762     align: '',
5763     inverse: false,
5764     form: false,
5765     type: 'nav',
5766     navId : '',
5767     // private
5768     
5769     navItems : false, 
5770     
5771     getAutoCreate : function()
5772     {
5773         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774         
5775         cfg = {
5776             tag : 'ul',
5777             cls: 'nav' 
5778         };
5779         if (Roo.bootstrap.version == 4) {
5780             if (['tabs','pills'].indexOf(this.type) != -1) {
5781                 cfg.cls += ' nav-' + this.type; 
5782             } else {
5783                 // trying to remove so header bar can right align top?
5784                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785                     // do not use on header bar... 
5786                     cfg.cls += ' navbar-nav';
5787                 }
5788             }
5789             
5790         } else {
5791             if (['tabs','pills'].indexOf(this.type) != -1) {
5792                 cfg.cls += ' nav-' + this.type
5793             } else {
5794                 if (this.type !== 'nav') {
5795                     Roo.log('nav type must be nav/tabs/pills')
5796                 }
5797                 cfg.cls += ' navbar-nav'
5798             }
5799         }
5800         
5801         if (this.parent() && this.parent().sidebar) {
5802             cfg = {
5803                 tag: 'ul',
5804                 cls: 'dashboard-menu sidebar-menu'
5805             };
5806             
5807             return cfg;
5808         }
5809         
5810         if (this.form === true) {
5811             cfg = {
5812                 tag: 'form',
5813                 cls: 'navbar-form form-inline'
5814             };
5815             //nav navbar-right ml-md-auto
5816             if (this.align === 'right') {
5817                 cfg.cls += ' navbar-right ml-md-auto';
5818             } else {
5819                 cfg.cls += ' navbar-left';
5820             }
5821         }
5822         
5823         if (this.align === 'right') {
5824             cfg.cls += ' navbar-right ml-md-auto';
5825         } else {
5826             cfg.cls += ' mr-auto';
5827         }
5828         
5829         if (this.inverse) {
5830             cfg.cls += ' navbar-inverse';
5831             
5832         }
5833         
5834         
5835         return cfg;
5836     },
5837     /**
5838     * sets the active Navigation item
5839     * @param {Roo.bootstrap.NavItem} the new current navitem
5840     */
5841     setActiveItem : function(item)
5842     {
5843         var prev = false;
5844         Roo.each(this.navItems, function(v){
5845             if (v == item) {
5846                 return ;
5847             }
5848             if (v.isActive()) {
5849                 v.setActive(false, true);
5850                 prev = v;
5851                 
5852             }
5853             
5854         });
5855
5856         item.setActive(true, true);
5857         this.fireEvent('changed', this, item, prev);
5858         
5859         
5860     },
5861     /**
5862     * gets the active Navigation item
5863     * @return {Roo.bootstrap.NavItem} the current navitem
5864     */
5865     getActive : function()
5866     {
5867         
5868         var prev = false;
5869         Roo.each(this.navItems, function(v){
5870             
5871             if (v.isActive()) {
5872                 prev = v;
5873                 
5874             }
5875             
5876         });
5877         return prev;
5878     },
5879     
5880     indexOfNav : function()
5881     {
5882         
5883         var prev = false;
5884         Roo.each(this.navItems, function(v,i){
5885             
5886             if (v.isActive()) {
5887                 prev = i;
5888                 
5889             }
5890             
5891         });
5892         return prev;
5893     },
5894     /**
5895     * adds a Navigation item
5896     * @param {Roo.bootstrap.NavItem} the navitem to add
5897     */
5898     addItem : function(cfg)
5899     {
5900         if (this.form && Roo.bootstrap.version == 4) {
5901             cfg.tag = 'div';
5902         }
5903         var cn = new Roo.bootstrap.NavItem(cfg);
5904         this.register(cn);
5905         cn.parentId = this.id;
5906         cn.onRender(this.el, null);
5907         return cn;
5908     },
5909     /**
5910     * register a Navigation item
5911     * @param {Roo.bootstrap.NavItem} the navitem to add
5912     */
5913     register : function(item)
5914     {
5915         this.navItems.push( item);
5916         item.navId = this.navId;
5917     
5918     },
5919     
5920     /**
5921     * clear all the Navigation item
5922     */
5923    
5924     clearAll : function()
5925     {
5926         this.navItems = [];
5927         this.el.dom.innerHTML = '';
5928     },
5929     
5930     getNavItem: function(tabId)
5931     {
5932         var ret = false;
5933         Roo.each(this.navItems, function(e) {
5934             if (e.tabId == tabId) {
5935                ret =  e;
5936                return false;
5937             }
5938             return true;
5939             
5940         });
5941         return ret;
5942     },
5943     
5944     setActiveNext : function()
5945     {
5946         var i = this.indexOfNav(this.getActive());
5947         if (i > this.navItems.length) {
5948             return;
5949         }
5950         this.setActiveItem(this.navItems[i+1]);
5951     },
5952     setActivePrev : function()
5953     {
5954         var i = this.indexOfNav(this.getActive());
5955         if (i  < 1) {
5956             return;
5957         }
5958         this.setActiveItem(this.navItems[i-1]);
5959     },
5960     clearWasActive : function(except) {
5961         Roo.each(this.navItems, function(e) {
5962             if (e.tabId != except.tabId && e.was_active) {
5963                e.was_active = false;
5964                return false;
5965             }
5966             return true;
5967             
5968         });
5969     },
5970     getWasActive : function ()
5971     {
5972         var r = false;
5973         Roo.each(this.navItems, function(e) {
5974             if (e.was_active) {
5975                r = e;
5976                return false;
5977             }
5978             return true;
5979             
5980         });
5981         return r;
5982     }
5983     
5984     
5985 });
5986
5987  
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5989     
5990     groups: {},
5991      /**
5992     * register a Navigation Group
5993     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5994     */
5995     register : function(navgrp)
5996     {
5997         this.groups[navgrp.navId] = navgrp;
5998         
5999     },
6000     /**
6001     * fetch a Navigation Group based on the navigation ID
6002     * @param {string} the navgroup to add
6003     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6004     */
6005     get: function(navId) {
6006         if (typeof(this.groups[navId]) == 'undefined') {
6007             return false;
6008             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6009         }
6010         return this.groups[navId] ;
6011     }
6012     
6013     
6014     
6015 });
6016
6017  /*
6018  * - LGPL
6019  *
6020  * row
6021  * 
6022  */
6023
6024 /**
6025  * @class Roo.bootstrap.NavItem
6026  * @extends Roo.bootstrap.Component
6027  * Bootstrap Navbar.NavItem class
6028  * @cfg {String} href  link to
6029  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6030
6031  * @cfg {String} html content of button
6032  * @cfg {String} badge text inside badge
6033  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6034  * @cfg {String} glyphicon DEPRICATED - use fa
6035  * @cfg {String} icon DEPRICATED - use fa
6036  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6037  * @cfg {Boolean} active Is item active
6038  * @cfg {Boolean} disabled Is item disabled
6039  
6040  * @cfg {Boolean} preventDefault (true | false) default false
6041  * @cfg {String} tabId the tab that this item activates.
6042  * @cfg {String} tagtype (a|span) render as a href or span?
6043  * @cfg {Boolean} animateRef (true|false) link to element default false  
6044   
6045  * @constructor
6046  * Create a new Navbar Item
6047  * @param {Object} config The config object
6048  */
6049 Roo.bootstrap.NavItem = function(config){
6050     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6051     this.addEvents({
6052         // raw events
6053         /**
6054          * @event click
6055          * The raw click event for the entire grid.
6056          * @param {Roo.EventObject} e
6057          */
6058         "click" : true,
6059          /**
6060             * @event changed
6061             * Fires when the active item active state changes
6062             * @param {Roo.bootstrap.NavItem} this
6063             * @param {boolean} state the new state
6064              
6065          */
6066         'changed': true,
6067         /**
6068             * @event scrollto
6069             * Fires when scroll to element
6070             * @param {Roo.bootstrap.NavItem} this
6071             * @param {Object} options
6072             * @param {Roo.EventObject} e
6073              
6074          */
6075         'scrollto': true
6076     });
6077    
6078 };
6079
6080 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6081     
6082     href: false,
6083     html: '',
6084     badge: '',
6085     icon: false,
6086     fa : false,
6087     glyphicon: false,
6088     active: false,
6089     preventDefault : false,
6090     tabId : false,
6091     tagtype : 'a',
6092     tag: 'li',
6093     disabled : false,
6094     animateRef : false,
6095     was_active : false,
6096     button_weight : '',
6097     button_outline : false,
6098     
6099     navLink: false,
6100     
6101     getAutoCreate : function(){
6102          
6103         var cfg = {
6104             tag: this.tag,
6105             cls: 'nav-item'
6106         };
6107         
6108         if (this.active) {
6109             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6110         }
6111         if (this.disabled) {
6112             cfg.cls += ' disabled';
6113         }
6114         
6115         // BS4 only?
6116         if (this.button_weight.length) {
6117             cfg.tag = this.href ? 'a' : 'button';
6118             cfg.html = this.html || '';
6119             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6120             if (this.href) {
6121                 cfg.href = this.href;
6122             }
6123             if (this.fa) {
6124                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6125             }
6126             
6127             // menu .. should add dropdown-menu class - so no need for carat..
6128             
6129             if (this.badge !== '') {
6130                  
6131                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6132             }
6133             return cfg;
6134         }
6135         
6136         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6137             cfg.cn = [
6138                 {
6139                     tag: this.tagtype,
6140                     href : this.href || "#",
6141                     html: this.html || ''
6142                 }
6143             ];
6144             if (this.tagtype == 'a') {
6145                 cfg.cn[0].cls = 'nav-link';
6146             }
6147             if (this.icon) {
6148                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6149             }
6150             if (this.fa) {
6151                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6152             }
6153             if(this.glyphicon) {
6154                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6155             }
6156             
6157             if (this.menu) {
6158                 
6159                 cfg.cn[0].html += " <span class='caret'></span>";
6160              
6161             }
6162             
6163             if (this.badge !== '') {
6164                  
6165                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6166             }
6167         }
6168         
6169         
6170         
6171         return cfg;
6172     },
6173     onRender : function(ct, position)
6174     {
6175        // Roo.log("Call onRender: " + this.xtype);
6176         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6177             this.tag = 'div';
6178         }
6179         
6180         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6181         this.navLink = this.el.select('.nav-link',true).first();
6182         return ret;
6183     },
6184       
6185     
6186     initEvents: function() 
6187     {
6188         if (typeof (this.menu) != 'undefined') {
6189             this.menu.parentType = this.xtype;
6190             this.menu.triggerEl = this.el;
6191             this.menu = this.addxtype(Roo.apply({}, this.menu));
6192         }
6193         
6194         this.el.select('a',true).on('click', this.onClick, this);
6195         
6196         if(this.tagtype == 'span'){
6197             this.el.select('span',true).on('click', this.onClick, this);
6198         }
6199        
6200         // at this point parent should be available..
6201         this.parent().register(this);
6202     },
6203     
6204     onClick : function(e)
6205     {
6206         if (e.getTarget('.dropdown-menu-item')) {
6207             // did you click on a menu itemm.... - then don't trigger onclick..
6208             return;
6209         }
6210         
6211         if(
6212                 this.preventDefault || 
6213                 this.href == '#' 
6214         ){
6215             Roo.log("NavItem - prevent Default?");
6216             e.preventDefault();
6217         }
6218         
6219         if (this.disabled) {
6220             return;
6221         }
6222         
6223         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6224         if (tg && tg.transition) {
6225             Roo.log("waiting for the transitionend");
6226             return;
6227         }
6228         
6229         
6230         
6231         //Roo.log("fire event clicked");
6232         if(this.fireEvent('click', this, e) === false){
6233             return;
6234         };
6235         
6236         if(this.tagtype == 'span'){
6237             return;
6238         }
6239         
6240         //Roo.log(this.href);
6241         var ael = this.el.select('a',true).first();
6242         //Roo.log(ael);
6243         
6244         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6245             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6246             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6247                 return; // ignore... - it's a 'hash' to another page.
6248             }
6249             Roo.log("NavItem - prevent Default?");
6250             e.preventDefault();
6251             this.scrollToElement(e);
6252         }
6253         
6254         
6255         var p =  this.parent();
6256    
6257         if (['tabs','pills'].indexOf(p.type)!==-1) {
6258             if (typeof(p.setActiveItem) !== 'undefined') {
6259                 p.setActiveItem(this);
6260             }
6261         }
6262         
6263         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6264         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6265             // remove the collapsed menu expand...
6266             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6267         }
6268     },
6269     
6270     isActive: function () {
6271         return this.active
6272     },
6273     setActive : function(state, fire, is_was_active)
6274     {
6275         if (this.active && !state && this.navId) {
6276             this.was_active = true;
6277             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6278             if (nv) {
6279                 nv.clearWasActive(this);
6280             }
6281             
6282         }
6283         this.active = state;
6284         
6285         if (!state ) {
6286             this.el.removeClass('active');
6287             this.navLink ? this.navLink.removeClass('active') : false;
6288         } else if (!this.el.hasClass('active')) {
6289             
6290             this.el.addClass('active');
6291             if (Roo.bootstrap.version == 4 && this.navLink ) {
6292                 this.navLink.addClass('active');
6293             }
6294             
6295         }
6296         if (fire) {
6297             this.fireEvent('changed', this, state);
6298         }
6299         
6300         // show a panel if it's registered and related..
6301         
6302         if (!this.navId || !this.tabId || !state || is_was_active) {
6303             return;
6304         }
6305         
6306         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6307         if (!tg) {
6308             return;
6309         }
6310         var pan = tg.getPanelByName(this.tabId);
6311         if (!pan) {
6312             return;
6313         }
6314         // if we can not flip to new panel - go back to old nav highlight..
6315         if (false == tg.showPanel(pan)) {
6316             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6317             if (nv) {
6318                 var onav = nv.getWasActive();
6319                 if (onav) {
6320                     onav.setActive(true, false, true);
6321                 }
6322             }
6323             
6324         }
6325         
6326         
6327         
6328     },
6329      // this should not be here...
6330     setDisabled : function(state)
6331     {
6332         this.disabled = state;
6333         if (!state ) {
6334             this.el.removeClass('disabled');
6335         } else if (!this.el.hasClass('disabled')) {
6336             this.el.addClass('disabled');
6337         }
6338         
6339     },
6340     
6341     /**
6342      * Fetch the element to display the tooltip on.
6343      * @return {Roo.Element} defaults to this.el
6344      */
6345     tooltipEl : function()
6346     {
6347         return this.el.select('' + this.tagtype + '', true).first();
6348     },
6349     
6350     scrollToElement : function(e)
6351     {
6352         var c = document.body;
6353         
6354         /*
6355          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6356          */
6357         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6358             c = document.documentElement;
6359         }
6360         
6361         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6362         
6363         if(!target){
6364             return;
6365         }
6366
6367         var o = target.calcOffsetsTo(c);
6368         
6369         var options = {
6370             target : target,
6371             value : o[1]
6372         };
6373         
6374         this.fireEvent('scrollto', this, options, e);
6375         
6376         Roo.get(c).scrollTo('top', options.value, true);
6377         
6378         return;
6379     }
6380 });
6381  
6382
6383  /*
6384  * - LGPL
6385  *
6386  * sidebar item
6387  *
6388  *  li
6389  *    <span> icon </span>
6390  *    <span> text </span>
6391  *    <span>badge </span>
6392  */
6393
6394 /**
6395  * @class Roo.bootstrap.NavSidebarItem
6396  * @extends Roo.bootstrap.NavItem
6397  * Bootstrap Navbar.NavSidebarItem class
6398  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6399  * {Boolean} open is the menu open
6400  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6401  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6402  * {String} buttonSize (sm|md|lg)the extra classes for the button
6403  * {Boolean} showArrow show arrow next to the text (default true)
6404  * @constructor
6405  * Create a new Navbar Button
6406  * @param {Object} config The config object
6407  */
6408 Roo.bootstrap.NavSidebarItem = function(config){
6409     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6410     this.addEvents({
6411         // raw events
6412         /**
6413          * @event click
6414          * The raw click event for the entire grid.
6415          * @param {Roo.EventObject} e
6416          */
6417         "click" : true,
6418          /**
6419             * @event changed
6420             * Fires when the active item active state changes
6421             * @param {Roo.bootstrap.NavSidebarItem} this
6422             * @param {boolean} state the new state
6423              
6424          */
6425         'changed': true
6426     });
6427    
6428 };
6429
6430 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6431     
6432     badgeWeight : 'default',
6433     
6434     open: false,
6435     
6436     buttonView : false,
6437     
6438     buttonWeight : 'default',
6439     
6440     buttonSize : 'md',
6441     
6442     showArrow : true,
6443     
6444     getAutoCreate : function(){
6445         
6446         
6447         var a = {
6448                 tag: 'a',
6449                 href : this.href || '#',
6450                 cls: '',
6451                 html : '',
6452                 cn : []
6453         };
6454         
6455         if(this.buttonView){
6456             a = {
6457                 tag: 'button',
6458                 href : this.href || '#',
6459                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6460                 html : this.html,
6461                 cn : []
6462             };
6463         }
6464         
6465         var cfg = {
6466             tag: 'li',
6467             cls: '',
6468             cn: [ a ]
6469         };
6470         
6471         if (this.active) {
6472             cfg.cls += ' active';
6473         }
6474         
6475         if (this.disabled) {
6476             cfg.cls += ' disabled';
6477         }
6478         if (this.open) {
6479             cfg.cls += ' open x-open';
6480         }
6481         // left icon..
6482         if (this.glyphicon || this.icon) {
6483             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6484             a.cn.push({ tag : 'i', cls : c }) ;
6485         }
6486         
6487         if(!this.buttonView){
6488             var span = {
6489                 tag: 'span',
6490                 html : this.html || ''
6491             };
6492
6493             a.cn.push(span);
6494             
6495         }
6496         
6497         if (this.badge !== '') {
6498             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6499         }
6500         
6501         if (this.menu) {
6502             
6503             if(this.showArrow){
6504                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6505             }
6506             
6507             a.cls += ' dropdown-toggle treeview' ;
6508         }
6509         
6510         return cfg;
6511     },
6512     
6513     initEvents : function()
6514     { 
6515         if (typeof (this.menu) != 'undefined') {
6516             this.menu.parentType = this.xtype;
6517             this.menu.triggerEl = this.el;
6518             this.menu = this.addxtype(Roo.apply({}, this.menu));
6519         }
6520         
6521         this.el.on('click', this.onClick, this);
6522         
6523         if(this.badge !== ''){
6524             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6525         }
6526         
6527     },
6528     
6529     onClick : function(e)
6530     {
6531         if(this.disabled){
6532             e.preventDefault();
6533             return;
6534         }
6535         
6536         if(this.preventDefault){
6537             e.preventDefault();
6538         }
6539         
6540         this.fireEvent('click', this, e);
6541     },
6542     
6543     disable : function()
6544     {
6545         this.setDisabled(true);
6546     },
6547     
6548     enable : function()
6549     {
6550         this.setDisabled(false);
6551     },
6552     
6553     setDisabled : function(state)
6554     {
6555         if(this.disabled == state){
6556             return;
6557         }
6558         
6559         this.disabled = state;
6560         
6561         if (state) {
6562             this.el.addClass('disabled');
6563             return;
6564         }
6565         
6566         this.el.removeClass('disabled');
6567         
6568         return;
6569     },
6570     
6571     setActive : function(state)
6572     {
6573         if(this.active == state){
6574             return;
6575         }
6576         
6577         this.active = state;
6578         
6579         if (state) {
6580             this.el.addClass('active');
6581             return;
6582         }
6583         
6584         this.el.removeClass('active');
6585         
6586         return;
6587     },
6588     
6589     isActive: function () 
6590     {
6591         return this.active;
6592     },
6593     
6594     setBadge : function(str)
6595     {
6596         if(!this.badgeEl){
6597             return;
6598         }
6599         
6600         this.badgeEl.dom.innerHTML = str;
6601     }
6602     
6603    
6604      
6605  
6606 });
6607  
6608
6609  /*
6610  * - LGPL
6611  *
6612  *  Breadcrumb Nav
6613  * 
6614  */
6615 Roo.namespace('Roo.bootstrap.breadcrumb');
6616
6617
6618 /**
6619  * @class Roo.bootstrap.breadcrumb.Nav
6620  * @extends Roo.bootstrap.Component
6621  * Bootstrap Breadcrumb Nav Class
6622  *  
6623  * @children Roo.bootstrap.breadcrumb.Item
6624  * 
6625  * @constructor
6626  * Create a new breadcrumb.Nav
6627  * @param {Object} config The config object
6628  */
6629
6630
6631 Roo.bootstrap.breadcrumb.Nav = function(config){
6632     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6633     
6634     
6635 };
6636
6637 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6638     
6639     getAutoCreate : function()
6640     {
6641
6642         var cfg = {
6643             tag: 'nav',
6644             cn : [
6645                 {
6646                     tag : 'ol',
6647                     cls : 'breadcrumb'
6648                 }
6649             ]
6650             
6651         };
6652           
6653         return cfg;
6654     },
6655     
6656     initEvents: function()
6657     {
6658         this.olEl = this.el.select('ol',true).first();    
6659     },
6660     getChildContainer : function()
6661     {
6662         return this.olEl;  
6663     }
6664     
6665 });
6666
6667  /*
6668  * - LGPL
6669  *
6670  *  Breadcrumb Item
6671  * 
6672  */
6673
6674
6675 /**
6676  * @class Roo.bootstrap.breadcrumb.Nav
6677  * @extends Roo.bootstrap.Component
6678  * Bootstrap Breadcrumb Nav Class
6679  *  
6680  * @children Roo.bootstrap.breadcrumb.Component
6681  * @cfg {String} html the content of the link.
6682  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6683  * @cfg {Boolean} active is it active
6684
6685  * 
6686  * @constructor
6687  * Create a new breadcrumb.Nav
6688  * @param {Object} config The config object
6689  */
6690
6691 Roo.bootstrap.breadcrumb.Item = function(config){
6692     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6693     this.addEvents({
6694         // img events
6695         /**
6696          * @event click
6697          * The img click event for the img.
6698          * @param {Roo.EventObject} e
6699          */
6700         "click" : true
6701     });
6702     
6703 };
6704
6705 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6706     
6707     href: false,
6708     html : '',
6709     
6710     getAutoCreate : function()
6711     {
6712
6713         var cfg = {
6714             tag: 'li',
6715             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6716         };
6717         if (this.href !== false) {
6718             cfg.cn = [{
6719                 tag : 'a',
6720                 href : this.href,
6721                 html : this.html
6722             }];
6723         } else {
6724             cfg.html = this.html;
6725         }
6726         
6727         return cfg;
6728     },
6729     
6730     initEvents: function()
6731     {
6732         if (this.href) {
6733             this.el.select('a', true).first().on('click',this.onClick, this)
6734         }
6735         
6736     },
6737     onClick : function(e)
6738     {
6739         e.preventDefault();
6740         this.fireEvent('click',this,  e);
6741     }
6742     
6743 });
6744
6745  /*
6746  * - LGPL
6747  *
6748  * row
6749  * 
6750  */
6751
6752 /**
6753  * @class Roo.bootstrap.Row
6754  * @extends Roo.bootstrap.Component
6755  * Bootstrap Row class (contains columns...)
6756  * 
6757  * @constructor
6758  * Create a new Row
6759  * @param {Object} config The config object
6760  */
6761
6762 Roo.bootstrap.Row = function(config){
6763     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6764 };
6765
6766 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6767     
6768     getAutoCreate : function(){
6769        return {
6770             cls: 'row clearfix'
6771        };
6772     }
6773     
6774     
6775 });
6776
6777  
6778
6779  /*
6780  * - LGPL
6781  *
6782  * pagination
6783  * 
6784  */
6785
6786 /**
6787  * @class Roo.bootstrap.Pagination
6788  * @extends Roo.bootstrap.Component
6789  * Bootstrap Pagination class
6790  * @cfg {String} size xs | sm | md | lg
6791  * @cfg {Boolean} inverse false | true
6792  * 
6793  * @constructor
6794  * Create a new Pagination
6795  * @param {Object} config The config object
6796  */
6797
6798 Roo.bootstrap.Pagination = function(config){
6799     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6800 };
6801
6802 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6803     
6804     cls: false,
6805     size: false,
6806     inverse: false,
6807     
6808     getAutoCreate : function(){
6809         var cfg = {
6810             tag: 'ul',
6811                 cls: 'pagination'
6812         };
6813         if (this.inverse) {
6814             cfg.cls += ' inverse';
6815         }
6816         if (this.html) {
6817             cfg.html=this.html;
6818         }
6819         if (this.cls) {
6820             cfg.cls += " " + this.cls;
6821         }
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  
6828
6829  /*
6830  * - LGPL
6831  *
6832  * Pagination item
6833  * 
6834  */
6835
6836
6837 /**
6838  * @class Roo.bootstrap.PaginationItem
6839  * @extends Roo.bootstrap.Component
6840  * Bootstrap PaginationItem class
6841  * @cfg {String} html text
6842  * @cfg {String} href the link
6843  * @cfg {Boolean} preventDefault (true | false) default true
6844  * @cfg {Boolean} active (true | false) default false
6845  * @cfg {Boolean} disabled default false
6846  * 
6847  * 
6848  * @constructor
6849  * Create a new PaginationItem
6850  * @param {Object} config The config object
6851  */
6852
6853
6854 Roo.bootstrap.PaginationItem = function(config){
6855     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6856     this.addEvents({
6857         // raw events
6858         /**
6859          * @event click
6860          * The raw click event for the entire grid.
6861          * @param {Roo.EventObject} e
6862          */
6863         "click" : true
6864     });
6865 };
6866
6867 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6868     
6869     href : false,
6870     html : false,
6871     preventDefault: true,
6872     active : false,
6873     cls : false,
6874     disabled: false,
6875     
6876     getAutoCreate : function(){
6877         var cfg= {
6878             tag: 'li',
6879             cn: [
6880                 {
6881                     tag : 'a',
6882                     href : this.href ? this.href : '#',
6883                     html : this.html ? this.html : ''
6884                 }
6885             ]
6886         };
6887         
6888         if(this.cls){
6889             cfg.cls = this.cls;
6890         }
6891         
6892         if(this.disabled){
6893             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6894         }
6895         
6896         if(this.active){
6897             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6898         }
6899         
6900         return cfg;
6901     },
6902     
6903     initEvents: function() {
6904         
6905         this.el.on('click', this.onClick, this);
6906         
6907     },
6908     onClick : function(e)
6909     {
6910         Roo.log('PaginationItem on click ');
6911         if(this.preventDefault){
6912             e.preventDefault();
6913         }
6914         
6915         if(this.disabled){
6916             return;
6917         }
6918         
6919         this.fireEvent('click', this, e);
6920     }
6921    
6922 });
6923
6924  
6925
6926  /*
6927  * - LGPL
6928  *
6929  * slider
6930  * 
6931  */
6932
6933
6934 /**
6935  * @class Roo.bootstrap.Slider
6936  * @extends Roo.bootstrap.Component
6937  * Bootstrap Slider class
6938  *    
6939  * @constructor
6940  * Create a new Slider
6941  * @param {Object} config The config object
6942  */
6943
6944 Roo.bootstrap.Slider = function(config){
6945     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6946 };
6947
6948 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6949     
6950     getAutoCreate : function(){
6951         
6952         var cfg = {
6953             tag: 'div',
6954             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6955             cn: [
6956                 {
6957                     tag: 'a',
6958                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6959                 }
6960             ]
6961         };
6962         
6963         return cfg;
6964     }
6965    
6966 });
6967
6968  /*
6969  * Based on:
6970  * Ext JS Library 1.1.1
6971  * Copyright(c) 2006-2007, Ext JS, LLC.
6972  *
6973  * Originally Released Under LGPL - original licence link has changed is not relivant.
6974  *
6975  * Fork - LGPL
6976  * <script type="text/javascript">
6977  */
6978  
6979
6980 /**
6981  * @class Roo.grid.ColumnModel
6982  * @extends Roo.util.Observable
6983  * This is the default implementation of a ColumnModel used by the Grid. It defines
6984  * the columns in the grid.
6985  * <br>Usage:<br>
6986  <pre><code>
6987  var colModel = new Roo.grid.ColumnModel([
6988         {header: "Ticker", width: 60, sortable: true, locked: true},
6989         {header: "Company Name", width: 150, sortable: true},
6990         {header: "Market Cap.", width: 100, sortable: true},
6991         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6992         {header: "Employees", width: 100, sortable: true, resizable: false}
6993  ]);
6994  </code></pre>
6995  * <p>
6996  
6997  * The config options listed for this class are options which may appear in each
6998  * individual column definition.
6999  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7000  * @constructor
7001  * @param {Object} config An Array of column config objects. See this class's
7002  * config objects for details.
7003 */
7004 Roo.grid.ColumnModel = function(config){
7005         /**
7006      * The config passed into the constructor
7007      */
7008     this.config = config;
7009     this.lookup = {};
7010
7011     // if no id, create one
7012     // if the column does not have a dataIndex mapping,
7013     // map it to the order it is in the config
7014     for(var i = 0, len = config.length; i < len; i++){
7015         var c = config[i];
7016         if(typeof c.dataIndex == "undefined"){
7017             c.dataIndex = i;
7018         }
7019         if(typeof c.renderer == "string"){
7020             c.renderer = Roo.util.Format[c.renderer];
7021         }
7022         if(typeof c.id == "undefined"){
7023             c.id = Roo.id();
7024         }
7025         if(c.editor && c.editor.xtype){
7026             c.editor  = Roo.factory(c.editor, Roo.grid);
7027         }
7028         if(c.editor && c.editor.isFormField){
7029             c.editor = new Roo.grid.GridEditor(c.editor);
7030         }
7031         this.lookup[c.id] = c;
7032     }
7033
7034     /**
7035      * The width of columns which have no width specified (defaults to 100)
7036      * @type Number
7037      */
7038     this.defaultWidth = 100;
7039
7040     /**
7041      * Default sortable of columns which have no sortable specified (defaults to false)
7042      * @type Boolean
7043      */
7044     this.defaultSortable = false;
7045
7046     this.addEvents({
7047         /**
7048              * @event widthchange
7049              * Fires when the width of a column changes.
7050              * @param {ColumnModel} this
7051              * @param {Number} columnIndex The column index
7052              * @param {Number} newWidth The new width
7053              */
7054             "widthchange": true,
7055         /**
7056              * @event headerchange
7057              * Fires when the text of a header changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newText The new header text
7061              */
7062             "headerchange": true,
7063         /**
7064              * @event hiddenchange
7065              * Fires when a column is hidden or "unhidden".
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Boolean} hidden true if hidden, false otherwise
7069              */
7070             "hiddenchange": true,
7071             /**
7072          * @event columnmoved
7073          * Fires when a column is moved.
7074          * @param {ColumnModel} this
7075          * @param {Number} oldIndex
7076          * @param {Number} newIndex
7077          */
7078         "columnmoved" : true,
7079         /**
7080          * @event columlockchange
7081          * Fires when a column's locked state is changed
7082          * @param {ColumnModel} this
7083          * @param {Number} colIndex
7084          * @param {Boolean} locked true if locked
7085          */
7086         "columnlockchange" : true
7087     });
7088     Roo.grid.ColumnModel.superclass.constructor.call(this);
7089 };
7090 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7091     /**
7092      * @cfg {String} header The header text to display in the Grid view.
7093      */
7094     /**
7095      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7096      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7097      * specified, the column's index is used as an index into the Record's data Array.
7098      */
7099     /**
7100      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7101      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7102      */
7103     /**
7104      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7105      * Defaults to the value of the {@link #defaultSortable} property.
7106      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7107      */
7108     /**
7109      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7110      */
7111     /**
7112      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7113      */
7114     /**
7115      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7116      */
7117     /**
7118      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7119      */
7120     /**
7121      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7122      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7123      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7124      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7125      */
7126        /**
7127      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7128      */
7129     /**
7130      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7131      */
7132     /**
7133      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7134      */
7135     /**
7136      * @cfg {String} cursor (Optional)
7137      */
7138     /**
7139      * @cfg {String} tooltip (Optional)
7140      */
7141     /**
7142      * @cfg {Number} xs (Optional)
7143      */
7144     /**
7145      * @cfg {Number} sm (Optional)
7146      */
7147     /**
7148      * @cfg {Number} md (Optional)
7149      */
7150     /**
7151      * @cfg {Number} lg (Optional)
7152      */
7153     /**
7154      * Returns the id of the column at the specified index.
7155      * @param {Number} index The column index
7156      * @return {String} the id
7157      */
7158     getColumnId : function(index){
7159         return this.config[index].id;
7160     },
7161
7162     /**
7163      * Returns the column for a specified id.
7164      * @param {String} id The column id
7165      * @return {Object} the column
7166      */
7167     getColumnById : function(id){
7168         return this.lookup[id];
7169     },
7170
7171     
7172     /**
7173      * Returns the column for a specified dataIndex.
7174      * @param {String} dataIndex The column dataIndex
7175      * @return {Object|Boolean} the column or false if not found
7176      */
7177     getColumnByDataIndex: function(dataIndex){
7178         var index = this.findColumnIndex(dataIndex);
7179         return index > -1 ? this.config[index] : false;
7180     },
7181     
7182     /**
7183      * Returns the index for a specified column id.
7184      * @param {String} id The column id
7185      * @return {Number} the index, or -1 if not found
7186      */
7187     getIndexById : function(id){
7188         for(var i = 0, len = this.config.length; i < len; i++){
7189             if(this.config[i].id == id){
7190                 return i;
7191             }
7192         }
7193         return -1;
7194     },
7195     
7196     /**
7197      * Returns the index for a specified column dataIndex.
7198      * @param {String} dataIndex The column dataIndex
7199      * @return {Number} the index, or -1 if not found
7200      */
7201     
7202     findColumnIndex : function(dataIndex){
7203         for(var i = 0, len = this.config.length; i < len; i++){
7204             if(this.config[i].dataIndex == dataIndex){
7205                 return i;
7206             }
7207         }
7208         return -1;
7209     },
7210     
7211     
7212     moveColumn : function(oldIndex, newIndex){
7213         var c = this.config[oldIndex];
7214         this.config.splice(oldIndex, 1);
7215         this.config.splice(newIndex, 0, c);
7216         this.dataMap = null;
7217         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7218     },
7219
7220     isLocked : function(colIndex){
7221         return this.config[colIndex].locked === true;
7222     },
7223
7224     setLocked : function(colIndex, value, suppressEvent){
7225         if(this.isLocked(colIndex) == value){
7226             return;
7227         }
7228         this.config[colIndex].locked = value;
7229         if(!suppressEvent){
7230             this.fireEvent("columnlockchange", this, colIndex, value);
7231         }
7232     },
7233
7234     getTotalLockedWidth : function(){
7235         var totalWidth = 0;
7236         for(var i = 0; i < this.config.length; i++){
7237             if(this.isLocked(i) && !this.isHidden(i)){
7238                 this.totalWidth += this.getColumnWidth(i);
7239             }
7240         }
7241         return totalWidth;
7242     },
7243
7244     getLockedCount : function(){
7245         for(var i = 0, len = this.config.length; i < len; i++){
7246             if(!this.isLocked(i)){
7247                 return i;
7248             }
7249         }
7250         
7251         return this.config.length;
7252     },
7253
7254     /**
7255      * Returns the number of columns.
7256      * @return {Number}
7257      */
7258     getColumnCount : function(visibleOnly){
7259         if(visibleOnly === true){
7260             var c = 0;
7261             for(var i = 0, len = this.config.length; i < len; i++){
7262                 if(!this.isHidden(i)){
7263                     c++;
7264                 }
7265             }
7266             return c;
7267         }
7268         return this.config.length;
7269     },
7270
7271     /**
7272      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7273      * @param {Function} fn
7274      * @param {Object} scope (optional)
7275      * @return {Array} result
7276      */
7277     getColumnsBy : function(fn, scope){
7278         var r = [];
7279         for(var i = 0, len = this.config.length; i < len; i++){
7280             var c = this.config[i];
7281             if(fn.call(scope||this, c, i) === true){
7282                 r[r.length] = c;
7283             }
7284         }
7285         return r;
7286     },
7287
7288     /**
7289      * Returns true if the specified column is sortable.
7290      * @param {Number} col The column index
7291      * @return {Boolean}
7292      */
7293     isSortable : function(col){
7294         if(typeof this.config[col].sortable == "undefined"){
7295             return this.defaultSortable;
7296         }
7297         return this.config[col].sortable;
7298     },
7299
7300     /**
7301      * Returns the rendering (formatting) function defined for the column.
7302      * @param {Number} col The column index.
7303      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7304      */
7305     getRenderer : function(col){
7306         if(!this.config[col].renderer){
7307             return Roo.grid.ColumnModel.defaultRenderer;
7308         }
7309         return this.config[col].renderer;
7310     },
7311
7312     /**
7313      * Sets the rendering (formatting) function for a column.
7314      * @param {Number} col The column index
7315      * @param {Function} fn The function to use to process the cell's raw data
7316      * to return HTML markup for the grid view. The render function is called with
7317      * the following parameters:<ul>
7318      * <li>Data value.</li>
7319      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7320      * <li>css A CSS style string to apply to the table cell.</li>
7321      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7322      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7323      * <li>Row index</li>
7324      * <li>Column index</li>
7325      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7326      */
7327     setRenderer : function(col, fn){
7328         this.config[col].renderer = fn;
7329     },
7330
7331     /**
7332      * Returns the width for the specified column.
7333      * @param {Number} col The column index
7334      * @return {Number}
7335      */
7336     getColumnWidth : function(col){
7337         return this.config[col].width * 1 || this.defaultWidth;
7338     },
7339
7340     /**
7341      * Sets the width for a column.
7342      * @param {Number} col The column index
7343      * @param {Number} width The new width
7344      */
7345     setColumnWidth : function(col, width, suppressEvent){
7346         this.config[col].width = width;
7347         this.totalWidth = null;
7348         if(!suppressEvent){
7349              this.fireEvent("widthchange", this, col, width);
7350         }
7351     },
7352
7353     /**
7354      * Returns the total width of all columns.
7355      * @param {Boolean} includeHidden True to include hidden column widths
7356      * @return {Number}
7357      */
7358     getTotalWidth : function(includeHidden){
7359         if(!this.totalWidth){
7360             this.totalWidth = 0;
7361             for(var i = 0, len = this.config.length; i < len; i++){
7362                 if(includeHidden || !this.isHidden(i)){
7363                     this.totalWidth += this.getColumnWidth(i);
7364                 }
7365             }
7366         }
7367         return this.totalWidth;
7368     },
7369
7370     /**
7371      * Returns the header for the specified column.
7372      * @param {Number} col The column index
7373      * @return {String}
7374      */
7375     getColumnHeader : function(col){
7376         return this.config[col].header;
7377     },
7378
7379     /**
7380      * Sets the header for a column.
7381      * @param {Number} col The column index
7382      * @param {String} header The new header
7383      */
7384     setColumnHeader : function(col, header){
7385         this.config[col].header = header;
7386         this.fireEvent("headerchange", this, col, header);
7387     },
7388
7389     /**
7390      * Returns the tooltip for the specified column.
7391      * @param {Number} col The column index
7392      * @return {String}
7393      */
7394     getColumnTooltip : function(col){
7395             return this.config[col].tooltip;
7396     },
7397     /**
7398      * Sets the tooltip for a column.
7399      * @param {Number} col The column index
7400      * @param {String} tooltip The new tooltip
7401      */
7402     setColumnTooltip : function(col, tooltip){
7403             this.config[col].tooltip = tooltip;
7404     },
7405
7406     /**
7407      * Returns the dataIndex for the specified column.
7408      * @param {Number} col The column index
7409      * @return {Number}
7410      */
7411     getDataIndex : function(col){
7412         return this.config[col].dataIndex;
7413     },
7414
7415     /**
7416      * Sets the dataIndex for a column.
7417      * @param {Number} col The column index
7418      * @param {Number} dataIndex The new dataIndex
7419      */
7420     setDataIndex : function(col, dataIndex){
7421         this.config[col].dataIndex = dataIndex;
7422     },
7423
7424     
7425     
7426     /**
7427      * Returns true if the cell is editable.
7428      * @param {Number} colIndex The column index
7429      * @param {Number} rowIndex The row index - this is nto actually used..?
7430      * @return {Boolean}
7431      */
7432     isCellEditable : function(colIndex, rowIndex){
7433         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7434     },
7435
7436     /**
7437      * Returns the editor defined for the cell/column.
7438      * return false or null to disable editing.
7439      * @param {Number} colIndex The column index
7440      * @param {Number} rowIndex The row index
7441      * @return {Object}
7442      */
7443     getCellEditor : function(colIndex, rowIndex){
7444         return this.config[colIndex].editor;
7445     },
7446
7447     /**
7448      * Sets if a column is editable.
7449      * @param {Number} col The column index
7450      * @param {Boolean} editable True if the column is editable
7451      */
7452     setEditable : function(col, editable){
7453         this.config[col].editable = editable;
7454     },
7455
7456
7457     /**
7458      * Returns true if the column is hidden.
7459      * @param {Number} colIndex The column index
7460      * @return {Boolean}
7461      */
7462     isHidden : function(colIndex){
7463         return this.config[colIndex].hidden;
7464     },
7465
7466
7467     /**
7468      * Returns true if the column width cannot be changed
7469      */
7470     isFixed : function(colIndex){
7471         return this.config[colIndex].fixed;
7472     },
7473
7474     /**
7475      * Returns true if the column can be resized
7476      * @return {Boolean}
7477      */
7478     isResizable : function(colIndex){
7479         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7480     },
7481     /**
7482      * Sets if a column is hidden.
7483      * @param {Number} colIndex The column index
7484      * @param {Boolean} hidden True if the column is hidden
7485      */
7486     setHidden : function(colIndex, hidden){
7487         this.config[colIndex].hidden = hidden;
7488         this.totalWidth = null;
7489         this.fireEvent("hiddenchange", this, colIndex, hidden);
7490     },
7491
7492     /**
7493      * Sets the editor for a column.
7494      * @param {Number} col The column index
7495      * @param {Object} editor The editor object
7496      */
7497     setEditor : function(col, editor){
7498         this.config[col].editor = editor;
7499     }
7500 });
7501
7502 Roo.grid.ColumnModel.defaultRenderer = function(value)
7503 {
7504     if(typeof value == "object") {
7505         return value;
7506     }
7507         if(typeof value == "string" && value.length < 1){
7508             return "&#160;";
7509         }
7510     
7511         return String.format("{0}", value);
7512 };
7513
7514 // Alias for backwards compatibility
7515 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7516 /*
7517  * Based on:
7518  * Ext JS Library 1.1.1
7519  * Copyright(c) 2006-2007, Ext JS, LLC.
7520  *
7521  * Originally Released Under LGPL - original licence link has changed is not relivant.
7522  *
7523  * Fork - LGPL
7524  * <script type="text/javascript">
7525  */
7526  
7527 /**
7528  * @class Roo.LoadMask
7529  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7530  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7531  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7532  * element's UpdateManager load indicator and will be destroyed after the initial load.
7533  * @constructor
7534  * Create a new LoadMask
7535  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7536  * @param {Object} config The config object
7537  */
7538 Roo.LoadMask = function(el, config){
7539     this.el = Roo.get(el);
7540     Roo.apply(this, config);
7541     if(this.store){
7542         this.store.on('beforeload', this.onBeforeLoad, this);
7543         this.store.on('load', this.onLoad, this);
7544         this.store.on('loadexception', this.onLoadException, this);
7545         this.removeMask = false;
7546     }else{
7547         var um = this.el.getUpdateManager();
7548         um.showLoadIndicator = false; // disable the default indicator
7549         um.on('beforeupdate', this.onBeforeLoad, this);
7550         um.on('update', this.onLoad, this);
7551         um.on('failure', this.onLoad, this);
7552         this.removeMask = true;
7553     }
7554 };
7555
7556 Roo.LoadMask.prototype = {
7557     /**
7558      * @cfg {Boolean} removeMask
7559      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7560      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7561      */
7562     /**
7563      * @cfg {String} msg
7564      * The text to display in a centered loading message box (defaults to 'Loading...')
7565      */
7566     msg : 'Loading...',
7567     /**
7568      * @cfg {String} msgCls
7569      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7570      */
7571     msgCls : 'x-mask-loading',
7572
7573     /**
7574      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7575      * @type Boolean
7576      */
7577     disabled: false,
7578
7579     /**
7580      * Disables the mask to prevent it from being displayed
7581      */
7582     disable : function(){
7583        this.disabled = true;
7584     },
7585
7586     /**
7587      * Enables the mask so that it can be displayed
7588      */
7589     enable : function(){
7590         this.disabled = false;
7591     },
7592     
7593     onLoadException : function()
7594     {
7595         Roo.log(arguments);
7596         
7597         if (typeof(arguments[3]) != 'undefined') {
7598             Roo.MessageBox.alert("Error loading",arguments[3]);
7599         } 
7600         /*
7601         try {
7602             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7603                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7604             }   
7605         } catch(e) {
7606             
7607         }
7608         */
7609     
7610         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7611     },
7612     // private
7613     onLoad : function()
7614     {
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617
7618     // private
7619     onBeforeLoad : function(){
7620         if(!this.disabled){
7621             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7622         }
7623     },
7624
7625     // private
7626     destroy : function(){
7627         if(this.store){
7628             this.store.un('beforeload', this.onBeforeLoad, this);
7629             this.store.un('load', this.onLoad, this);
7630             this.store.un('loadexception', this.onLoadException, this);
7631         }else{
7632             var um = this.el.getUpdateManager();
7633             um.un('beforeupdate', this.onBeforeLoad, this);
7634             um.un('update', this.onLoad, this);
7635             um.un('failure', this.onLoad, this);
7636         }
7637     }
7638 };/*
7639  * - LGPL
7640  *
7641  * table
7642  * 
7643  */
7644
7645 /**
7646  * @class Roo.bootstrap.Table
7647  * @extends Roo.bootstrap.Component
7648  * Bootstrap Table class
7649  * @cfg {String} cls table class
7650  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7651  * @cfg {String} bgcolor Specifies the background color for a table
7652  * @cfg {Number} border Specifies whether the table cells should have borders or not
7653  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7654  * @cfg {Number} cellspacing Specifies the space between cells
7655  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7656  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7657  * @cfg {String} sortable Specifies that the table should be sortable
7658  * @cfg {String} summary Specifies a summary of the content of a table
7659  * @cfg {Number} width Specifies the width of a table
7660  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7661  * 
7662  * @cfg {boolean} striped Should the rows be alternative striped
7663  * @cfg {boolean} bordered Add borders to the table
7664  * @cfg {boolean} hover Add hover highlighting
7665  * @cfg {boolean} condensed Format condensed
7666  * @cfg {boolean} responsive Format condensed
7667  * @cfg {Boolean} loadMask (true|false) default false
7668  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7669  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7670  * @cfg {Boolean} rowSelection (true|false) default false
7671  * @cfg {Boolean} cellSelection (true|false) default false
7672  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7673  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7674  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7675  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7676  
7677  * 
7678  * @constructor
7679  * Create a new Table
7680  * @param {Object} config The config object
7681  */
7682
7683 Roo.bootstrap.Table = function(config){
7684     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7685     
7686   
7687     
7688     // BC...
7689     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7690     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7691     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7692     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7693     
7694     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7695     if (this.sm) {
7696         this.sm.grid = this;
7697         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7698         this.sm = this.selModel;
7699         this.sm.xmodule = this.xmodule || false;
7700     }
7701     
7702     if (this.cm && typeof(this.cm.config) == 'undefined') {
7703         this.colModel = new Roo.grid.ColumnModel(this.cm);
7704         this.cm = this.colModel;
7705         this.cm.xmodule = this.xmodule || false;
7706     }
7707     if (this.store) {
7708         this.store= Roo.factory(this.store, Roo.data);
7709         this.ds = this.store;
7710         this.ds.xmodule = this.xmodule || false;
7711          
7712     }
7713     if (this.footer && this.store) {
7714         this.footer.dataSource = this.ds;
7715         this.footer = Roo.factory(this.footer);
7716     }
7717     
7718     /** @private */
7719     this.addEvents({
7720         /**
7721          * @event cellclick
7722          * Fires when a cell is clicked
7723          * @param {Roo.bootstrap.Table} this
7724          * @param {Roo.Element} el
7725          * @param {Number} rowIndex
7726          * @param {Number} columnIndex
7727          * @param {Roo.EventObject} e
7728          */
7729         "cellclick" : true,
7730         /**
7731          * @event celldblclick
7732          * Fires when a cell is double clicked
7733          * @param {Roo.bootstrap.Table} this
7734          * @param {Roo.Element} el
7735          * @param {Number} rowIndex
7736          * @param {Number} columnIndex
7737          * @param {Roo.EventObject} e
7738          */
7739         "celldblclick" : true,
7740         /**
7741          * @event rowclick
7742          * Fires when a row is clicked
7743          * @param {Roo.bootstrap.Table} this
7744          * @param {Roo.Element} el
7745          * @param {Number} rowIndex
7746          * @param {Roo.EventObject} e
7747          */
7748         "rowclick" : true,
7749         /**
7750          * @event rowdblclick
7751          * Fires when a row is double clicked
7752          * @param {Roo.bootstrap.Table} this
7753          * @param {Roo.Element} el
7754          * @param {Number} rowIndex
7755          * @param {Roo.EventObject} e
7756          */
7757         "rowdblclick" : true,
7758         /**
7759          * @event mouseover
7760          * Fires when a mouseover occur
7761          * @param {Roo.bootstrap.Table} this
7762          * @param {Roo.Element} el
7763          * @param {Number} rowIndex
7764          * @param {Number} columnIndex
7765          * @param {Roo.EventObject} e
7766          */
7767         "mouseover" : true,
7768         /**
7769          * @event mouseout
7770          * Fires when a mouseout occur
7771          * @param {Roo.bootstrap.Table} this
7772          * @param {Roo.Element} el
7773          * @param {Number} rowIndex
7774          * @param {Number} columnIndex
7775          * @param {Roo.EventObject} e
7776          */
7777         "mouseout" : true,
7778         /**
7779          * @event rowclass
7780          * Fires when a row is rendered, so you can change add a style to it.
7781          * @param {Roo.bootstrap.Table} this
7782          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7783          */
7784         'rowclass' : true,
7785           /**
7786          * @event rowsrendered
7787          * Fires when all the  rows have been rendered
7788          * @param {Roo.bootstrap.Table} this
7789          */
7790         'rowsrendered' : true,
7791         /**
7792          * @event contextmenu
7793          * The raw contextmenu event for the entire grid.
7794          * @param {Roo.EventObject} e
7795          */
7796         "contextmenu" : true,
7797         /**
7798          * @event rowcontextmenu
7799          * Fires when a row is right clicked
7800          * @param {Roo.bootstrap.Table} this
7801          * @param {Number} rowIndex
7802          * @param {Roo.EventObject} e
7803          */
7804         "rowcontextmenu" : true,
7805         /**
7806          * @event cellcontextmenu
7807          * Fires when a cell is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Number} cellIndex
7811          * @param {Roo.EventObject} e
7812          */
7813          "cellcontextmenu" : true,
7814          /**
7815          * @event headercontextmenu
7816          * Fires when a header is right clicked
7817          * @param {Roo.bootstrap.Table} this
7818          * @param {Number} columnIndex
7819          * @param {Roo.EventObject} e
7820          */
7821         "headercontextmenu" : true
7822     });
7823 };
7824
7825 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7826     
7827     cls: false,
7828     align: false,
7829     bgcolor: false,
7830     border: false,
7831     cellpadding: false,
7832     cellspacing: false,
7833     frame: false,
7834     rules: false,
7835     sortable: false,
7836     summary: false,
7837     width: false,
7838     striped : false,
7839     scrollBody : false,
7840     bordered: false,
7841     hover:  false,
7842     condensed : false,
7843     responsive : false,
7844     sm : false,
7845     cm : false,
7846     store : false,
7847     loadMask : false,
7848     footerShow : true,
7849     headerShow : true,
7850   
7851     rowSelection : false,
7852     cellSelection : false,
7853     layout : false,
7854     
7855     // Roo.Element - the tbody
7856     mainBody: false,
7857     // Roo.Element - thead element
7858     mainHead: false,
7859     
7860     container: false, // used by gridpanel...
7861     
7862     lazyLoad : false,
7863     
7864     CSS : Roo.util.CSS,
7865     
7866     auto_hide_footer : false,
7867     
7868     getAutoCreate : function()
7869     {
7870         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7871         
7872         cfg = {
7873             tag: 'table',
7874             cls : 'table',
7875             cn : []
7876         };
7877         if (this.scrollBody) {
7878             cfg.cls += ' table-body-fixed';
7879         }    
7880         if (this.striped) {
7881             cfg.cls += ' table-striped';
7882         }
7883         
7884         if (this.hover) {
7885             cfg.cls += ' table-hover';
7886         }
7887         if (this.bordered) {
7888             cfg.cls += ' table-bordered';
7889         }
7890         if (this.condensed) {
7891             cfg.cls += ' table-condensed';
7892         }
7893         if (this.responsive) {
7894             cfg.cls += ' table-responsive';
7895         }
7896         
7897         if (this.cls) {
7898             cfg.cls+=  ' ' +this.cls;
7899         }
7900         
7901         // this lot should be simplifed...
7902         var _t = this;
7903         var cp = [
7904             'align',
7905             'bgcolor',
7906             'border',
7907             'cellpadding',
7908             'cellspacing',
7909             'frame',
7910             'rules',
7911             'sortable',
7912             'summary',
7913             'width'
7914         ].forEach(function(k) {
7915             if (_t[k]) {
7916                 cfg[k] = _t[k];
7917             }
7918         });
7919         
7920         
7921         if (this.layout) {
7922             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7923         }
7924         
7925         if(this.store || this.cm){
7926             if(this.headerShow){
7927                 cfg.cn.push(this.renderHeader());
7928             }
7929             
7930             cfg.cn.push(this.renderBody());
7931             
7932             if(this.footerShow){
7933                 cfg.cn.push(this.renderFooter());
7934             }
7935             // where does this come from?
7936             //cfg.cls+=  ' TableGrid';
7937         }
7938         
7939         return { cn : [ cfg ] };
7940     },
7941     
7942     initEvents : function()
7943     {   
7944         if(!this.store || !this.cm){
7945             return;
7946         }
7947         if (this.selModel) {
7948             this.selModel.initEvents();
7949         }
7950         
7951         
7952         //Roo.log('initEvents with ds!!!!');
7953         
7954         this.mainBody = this.el.select('tbody', true).first();
7955         this.mainHead = this.el.select('thead', true).first();
7956         this.mainFoot = this.el.select('tfoot', true).first();
7957         
7958         
7959         
7960         var _this = this;
7961         
7962         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7963             e.on('click', _this.sort, _this);
7964         });
7965         
7966         this.mainBody.on("click", this.onClick, this);
7967         this.mainBody.on("dblclick", this.onDblClick, this);
7968         
7969         // why is this done????? = it breaks dialogs??
7970         //this.parent().el.setStyle('position', 'relative');
7971         
7972         
7973         if (this.footer) {
7974             this.footer.parentId = this.id;
7975             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7976             
7977             if(this.lazyLoad){
7978                 this.el.select('tfoot tr td').first().addClass('hide');
7979             }
7980         } 
7981         
7982         if(this.loadMask) {
7983             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7984         }
7985         
7986         this.store.on('load', this.onLoad, this);
7987         this.store.on('beforeload', this.onBeforeLoad, this);
7988         this.store.on('update', this.onUpdate, this);
7989         this.store.on('add', this.onAdd, this);
7990         this.store.on("clear", this.clear, this);
7991         
7992         this.el.on("contextmenu", this.onContextMenu, this);
7993         
7994         this.mainBody.on('scroll', this.onBodyScroll, this);
7995         
7996         this.cm.on("headerchange", this.onHeaderChange, this);
7997         
7998         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7999         
8000     },
8001     
8002     onContextMenu : function(e, t)
8003     {
8004         this.processEvent("contextmenu", e);
8005     },
8006     
8007     processEvent : function(name, e)
8008     {
8009         if (name != 'touchstart' ) {
8010             this.fireEvent(name, e);    
8011         }
8012         
8013         var t = e.getTarget();
8014         
8015         var cell = Roo.get(t);
8016         
8017         if(!cell){
8018             return;
8019         }
8020         
8021         if(cell.findParent('tfoot', false, true)){
8022             return;
8023         }
8024         
8025         if(cell.findParent('thead', false, true)){
8026             
8027             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8028                 cell = Roo.get(t).findParent('th', false, true);
8029                 if (!cell) {
8030                     Roo.log("failed to find th in thead?");
8031                     Roo.log(e.getTarget());
8032                     return;
8033                 }
8034             }
8035             
8036             var cellIndex = cell.dom.cellIndex;
8037             
8038             var ename = name == 'touchstart' ? 'click' : name;
8039             this.fireEvent("header" + ename, this, cellIndex, e);
8040             
8041             return;
8042         }
8043         
8044         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8045             cell = Roo.get(t).findParent('td', false, true);
8046             if (!cell) {
8047                 Roo.log("failed to find th in tbody?");
8048                 Roo.log(e.getTarget());
8049                 return;
8050             }
8051         }
8052         
8053         var row = cell.findParent('tr', false, true);
8054         var cellIndex = cell.dom.cellIndex;
8055         var rowIndex = row.dom.rowIndex - 1;
8056         
8057         if(row !== false){
8058             
8059             this.fireEvent("row" + name, this, rowIndex, e);
8060             
8061             if(cell !== false){
8062             
8063                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8064             }
8065         }
8066         
8067     },
8068     
8069     onMouseover : function(e, el)
8070     {
8071         var cell = Roo.get(el);
8072         
8073         if(!cell){
8074             return;
8075         }
8076         
8077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8078             cell = cell.findParent('td', false, true);
8079         }
8080         
8081         var row = cell.findParent('tr', false, true);
8082         var cellIndex = cell.dom.cellIndex;
8083         var rowIndex = row.dom.rowIndex - 1; // start from 0
8084         
8085         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8086         
8087     },
8088     
8089     onMouseout : function(e, el)
8090     {
8091         var cell = Roo.get(el);
8092         
8093         if(!cell){
8094             return;
8095         }
8096         
8097         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8098             cell = cell.findParent('td', false, true);
8099         }
8100         
8101         var row = cell.findParent('tr', false, true);
8102         var cellIndex = cell.dom.cellIndex;
8103         var rowIndex = row.dom.rowIndex - 1; // start from 0
8104         
8105         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8106         
8107     },
8108     
8109     onClick : function(e, el)
8110     {
8111         var cell = Roo.get(el);
8112         
8113         if(!cell || (!this.cellSelection && !this.rowSelection)){
8114             return;
8115         }
8116         
8117         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8118             cell = cell.findParent('td', false, true);
8119         }
8120         
8121         if(!cell || typeof(cell) == 'undefined'){
8122             return;
8123         }
8124         
8125         var row = cell.findParent('tr', false, true);
8126         
8127         if(!row || typeof(row) == 'undefined'){
8128             return;
8129         }
8130         
8131         var cellIndex = cell.dom.cellIndex;
8132         var rowIndex = this.getRowIndex(row);
8133         
8134         // why??? - should these not be based on SelectionModel?
8135         if(this.cellSelection){
8136             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8137         }
8138         
8139         if(this.rowSelection){
8140             this.fireEvent('rowclick', this, row, rowIndex, e);
8141         }
8142         
8143         
8144     },
8145         
8146     onDblClick : function(e,el)
8147     {
8148         var cell = Roo.get(el);
8149         
8150         if(!cell || (!this.cellSelection && !this.rowSelection)){
8151             return;
8152         }
8153         
8154         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8155             cell = cell.findParent('td', false, true);
8156         }
8157         
8158         if(!cell || typeof(cell) == 'undefined'){
8159             return;
8160         }
8161         
8162         var row = cell.findParent('tr', false, true);
8163         
8164         if(!row || typeof(row) == 'undefined'){
8165             return;
8166         }
8167         
8168         var cellIndex = cell.dom.cellIndex;
8169         var rowIndex = this.getRowIndex(row);
8170         
8171         if(this.cellSelection){
8172             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8173         }
8174         
8175         if(this.rowSelection){
8176             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8177         }
8178     },
8179     
8180     sort : function(e,el)
8181     {
8182         var col = Roo.get(el);
8183         
8184         if(!col.hasClass('sortable')){
8185             return;
8186         }
8187         
8188         var sort = col.attr('sort');
8189         var dir = 'ASC';
8190         
8191         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8192             dir = 'DESC';
8193         }
8194         
8195         this.store.sortInfo = {field : sort, direction : dir};
8196         
8197         if (this.footer) {
8198             Roo.log("calling footer first");
8199             this.footer.onClick('first');
8200         } else {
8201         
8202             this.store.load({ params : { start : 0 } });
8203         }
8204     },
8205     
8206     renderHeader : function()
8207     {
8208         var header = {
8209             tag: 'thead',
8210             cn : []
8211         };
8212         
8213         var cm = this.cm;
8214         this.totalWidth = 0;
8215         
8216         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8217             
8218             var config = cm.config[i];
8219             
8220             var c = {
8221                 tag: 'th',
8222                 cls : 'x-hcol-' + i,
8223                 style : '',
8224                 html: cm.getColumnHeader(i)
8225             };
8226             
8227             var hh = '';
8228             
8229             if(typeof(config.sortable) != 'undefined' && config.sortable){
8230                 c.cls = 'sortable';
8231                 c.html = '<i class="glyphicon"></i>' + c.html;
8232             }
8233             
8234             // could use BS4 hidden-..-down 
8235             
8236             if(typeof(config.lgHeader) != 'undefined'){
8237                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8238             }
8239             
8240             if(typeof(config.mdHeader) != 'undefined'){
8241                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8242             }
8243             
8244             if(typeof(config.smHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.xsHeader) != 'undefined'){
8249                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8250             }
8251             
8252             if(hh.length){
8253                 c.html = hh;
8254             }
8255             
8256             if(typeof(config.tooltip) != 'undefined'){
8257                 c.tooltip = config.tooltip;
8258             }
8259             
8260             if(typeof(config.colspan) != 'undefined'){
8261                 c.colspan = config.colspan;
8262             }
8263             
8264             if(typeof(config.hidden) != 'undefined' && config.hidden){
8265                 c.style += ' display:none;';
8266             }
8267             
8268             if(typeof(config.dataIndex) != 'undefined'){
8269                 c.sort = config.dataIndex;
8270             }
8271             
8272            
8273             
8274             if(typeof(config.align) != 'undefined' && config.align.length){
8275                 c.style += ' text-align:' + config.align + ';';
8276             }
8277             
8278             if(typeof(config.width) != 'undefined'){
8279                 c.style += ' width:' + config.width + 'px;';
8280                 this.totalWidth += config.width;
8281             } else {
8282                 this.totalWidth += 100; // assume minimum of 100 per column?
8283             }
8284             
8285             if(typeof(config.cls) != 'undefined'){
8286                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8287             }
8288             
8289             ['xs','sm','md','lg'].map(function(size){
8290                 
8291                 if(typeof(config[size]) == 'undefined'){
8292                     return;
8293                 }
8294                  
8295                 if (!config[size]) { // 0 = hidden
8296                     // BS 4 '0' is treated as hide that column and below.
8297                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8298                     return;
8299                 }
8300                 
8301                 c.cls += ' col-' + size + '-' + config[size] + (
8302                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8303                 );
8304                 
8305                 
8306             });
8307             
8308             header.cn.push(c)
8309         }
8310         
8311         return header;
8312     },
8313     
8314     renderBody : function()
8315     {
8316         var body = {
8317             tag: 'tbody',
8318             cn : [
8319                 {
8320                     tag: 'tr',
8321                     cn : [
8322                         {
8323                             tag : 'td',
8324                             colspan :  this.cm.getColumnCount()
8325                         }
8326                     ]
8327                 }
8328             ]
8329         };
8330         
8331         return body;
8332     },
8333     
8334     renderFooter : function()
8335     {
8336         var footer = {
8337             tag: 'tfoot',
8338             cn : [
8339                 {
8340                     tag: 'tr',
8341                     cn : [
8342                         {
8343                             tag : 'td',
8344                             colspan :  this.cm.getColumnCount()
8345                         }
8346                     ]
8347                 }
8348             ]
8349         };
8350         
8351         return footer;
8352     },
8353     
8354     
8355     
8356     onLoad : function()
8357     {
8358 //        Roo.log('ds onload');
8359         this.clear();
8360         
8361         var _this = this;
8362         var cm = this.cm;
8363         var ds = this.store;
8364         
8365         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8366             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8367             if (_this.store.sortInfo) {
8368                     
8369                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8370                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8371                 }
8372                 
8373                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8374                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8375                 }
8376             }
8377         });
8378         
8379         var tbody =  this.mainBody;
8380               
8381         if(ds.getCount() > 0){
8382             ds.data.each(function(d,rowIndex){
8383                 var row =  this.renderRow(cm, ds, rowIndex);
8384                 
8385                 tbody.createChild(row);
8386                 
8387                 var _this = this;
8388                 
8389                 if(row.cellObjects.length){
8390                     Roo.each(row.cellObjects, function(r){
8391                         _this.renderCellObject(r);
8392                     })
8393                 }
8394                 
8395             }, this);
8396         }
8397         
8398         var tfoot = this.el.select('tfoot', true).first();
8399         
8400         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8401             
8402             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8403             
8404             var total = this.ds.getTotalCount();
8405             
8406             if(this.footer.pageSize < total){
8407                 this.mainFoot.show();
8408             }
8409         }
8410         
8411         Roo.each(this.el.select('tbody td', true).elements, function(e){
8412             e.on('mouseover', _this.onMouseover, _this);
8413         });
8414         
8415         Roo.each(this.el.select('tbody td', true).elements, function(e){
8416             e.on('mouseout', _this.onMouseout, _this);
8417         });
8418         this.fireEvent('rowsrendered', this);
8419         
8420         this.autoSize();
8421     },
8422     
8423     
8424     onUpdate : function(ds,record)
8425     {
8426         this.refreshRow(record);
8427         this.autoSize();
8428     },
8429     
8430     onRemove : function(ds, record, index, isUpdate){
8431         if(isUpdate !== true){
8432             this.fireEvent("beforerowremoved", this, index, record);
8433         }
8434         var bt = this.mainBody.dom;
8435         
8436         var rows = this.el.select('tbody > tr', true).elements;
8437         
8438         if(typeof(rows[index]) != 'undefined'){
8439             bt.removeChild(rows[index].dom);
8440         }
8441         
8442 //        if(bt.rows[index]){
8443 //            bt.removeChild(bt.rows[index]);
8444 //        }
8445         
8446         if(isUpdate !== true){
8447             //this.stripeRows(index);
8448             //this.syncRowHeights(index, index);
8449             //this.layout();
8450             this.fireEvent("rowremoved", this, index, record);
8451         }
8452     },
8453     
8454     onAdd : function(ds, records, rowIndex)
8455     {
8456         //Roo.log('on Add called');
8457         // - note this does not handle multiple adding very well..
8458         var bt = this.mainBody.dom;
8459         for (var i =0 ; i < records.length;i++) {
8460             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8461             //Roo.log(records[i]);
8462             //Roo.log(this.store.getAt(rowIndex+i));
8463             this.insertRow(this.store, rowIndex + i, false);
8464             return;
8465         }
8466         
8467     },
8468     
8469     
8470     refreshRow : function(record){
8471         var ds = this.store, index;
8472         if(typeof record == 'number'){
8473             index = record;
8474             record = ds.getAt(index);
8475         }else{
8476             index = ds.indexOf(record);
8477             if (index < 0) {
8478                 return; // should not happen - but seems to 
8479             }
8480         }
8481         this.insertRow(ds, index, true);
8482         this.autoSize();
8483         this.onRemove(ds, record, index+1, true);
8484         this.autoSize();
8485         //this.syncRowHeights(index, index);
8486         //this.layout();
8487         this.fireEvent("rowupdated", this, index, record);
8488     },
8489     
8490     insertRow : function(dm, rowIndex, isUpdate){
8491         
8492         if(!isUpdate){
8493             this.fireEvent("beforerowsinserted", this, rowIndex);
8494         }
8495             //var s = this.getScrollState();
8496         var row = this.renderRow(this.cm, this.store, rowIndex);
8497         // insert before rowIndex..
8498         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8499         
8500         var _this = this;
8501                 
8502         if(row.cellObjects.length){
8503             Roo.each(row.cellObjects, function(r){
8504                 _this.renderCellObject(r);
8505             })
8506         }
8507             
8508         if(!isUpdate){
8509             this.fireEvent("rowsinserted", this, rowIndex);
8510             //this.syncRowHeights(firstRow, lastRow);
8511             //this.stripeRows(firstRow);
8512             //this.layout();
8513         }
8514         
8515     },
8516     
8517     
8518     getRowDom : function(rowIndex)
8519     {
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8523         
8524     },
8525     // returns the object tree for a tr..
8526   
8527     
8528     renderRow : function(cm, ds, rowIndex) 
8529     {
8530         var d = ds.getAt(rowIndex);
8531         
8532         var row = {
8533             tag : 'tr',
8534             cls : 'x-row-' + rowIndex,
8535             cn : []
8536         };
8537             
8538         var cellObjects = [];
8539         
8540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8541             var config = cm.config[i];
8542             
8543             var renderer = cm.getRenderer(i);
8544             var value = '';
8545             var id = false;
8546             
8547             if(typeof(renderer) !== 'undefined'){
8548                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8549             }
8550             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8551             // and are rendered into the cells after the row is rendered - using the id for the element.
8552             
8553             if(typeof(value) === 'object'){
8554                 id = Roo.id();
8555                 cellObjects.push({
8556                     container : id,
8557                     cfg : value 
8558                 })
8559             }
8560             
8561             var rowcfg = {
8562                 record: d,
8563                 rowIndex : rowIndex,
8564                 colIndex : i,
8565                 rowClass : ''
8566             };
8567
8568             this.fireEvent('rowclass', this, rowcfg);
8569             
8570             var td = {
8571                 tag: 'td',
8572                 cls : rowcfg.rowClass + ' x-col-' + i,
8573                 style: '',
8574                 html: (typeof(value) === 'object') ? '' : value
8575             };
8576             
8577             if (id) {
8578                 td.id = id;
8579             }
8580             
8581             if(typeof(config.colspan) != 'undefined'){
8582                 td.colspan = config.colspan;
8583             }
8584             
8585             if(typeof(config.hidden) != 'undefined' && config.hidden){
8586                 td.style += ' display:none;';
8587             }
8588             
8589             if(typeof(config.align) != 'undefined' && config.align.length){
8590                 td.style += ' text-align:' + config.align + ';';
8591             }
8592             if(typeof(config.valign) != 'undefined' && config.valign.length){
8593                 td.style += ' vertical-align:' + config.valign + ';';
8594             }
8595             
8596             if(typeof(config.width) != 'undefined'){
8597                 td.style += ' width:' +  config.width + 'px;';
8598             }
8599             
8600             if(typeof(config.cursor) != 'undefined'){
8601                 td.style += ' cursor:' +  config.cursor + ';';
8602             }
8603             
8604             if(typeof(config.cls) != 'undefined'){
8605                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8606             }
8607             
8608             ['xs','sm','md','lg'].map(function(size){
8609                 
8610                 if(typeof(config[size]) == 'undefined'){
8611                     return;
8612                 }
8613                 
8614                 
8615                   
8616                 if (!config[size]) { // 0 = hidden
8617                     // BS 4 '0' is treated as hide that column and below.
8618                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8619                     return;
8620                 }
8621                 
8622                 td.cls += ' col-' + size + '-' + config[size] + (
8623                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8624                 );
8625                  
8626
8627             });
8628             
8629             row.cn.push(td);
8630            
8631         }
8632         
8633         row.cellObjects = cellObjects;
8634         
8635         return row;
8636           
8637     },
8638     
8639     
8640     
8641     onBeforeLoad : function()
8642     {
8643         
8644     },
8645      /**
8646      * Remove all rows
8647      */
8648     clear : function()
8649     {
8650         this.el.select('tbody', true).first().dom.innerHTML = '';
8651     },
8652     /**
8653      * Show or hide a row.
8654      * @param {Number} rowIndex to show or hide
8655      * @param {Boolean} state hide
8656      */
8657     setRowVisibility : function(rowIndex, state)
8658     {
8659         var bt = this.mainBody.dom;
8660         
8661         var rows = this.el.select('tbody > tr', true).elements;
8662         
8663         if(typeof(rows[rowIndex]) == 'undefined'){
8664             return;
8665         }
8666         rows[rowIndex].dom.style.display = state ? '' : 'none';
8667     },
8668     
8669     
8670     getSelectionModel : function(){
8671         if(!this.selModel){
8672             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8673         }
8674         return this.selModel;
8675     },
8676     /*
8677      * Render the Roo.bootstrap object from renderder
8678      */
8679     renderCellObject : function(r)
8680     {
8681         var _this = this;
8682         
8683         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8684         
8685         var t = r.cfg.render(r.container);
8686         
8687         if(r.cfg.cn){
8688             Roo.each(r.cfg.cn, function(c){
8689                 var child = {
8690                     container: t.getChildContainer(),
8691                     cfg: c
8692                 };
8693                 _this.renderCellObject(child);
8694             })
8695         }
8696     },
8697     
8698     getRowIndex : function(row)
8699     {
8700         var rowIndex = -1;
8701         
8702         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8703             if(el != row){
8704                 return;
8705             }
8706             
8707             rowIndex = index;
8708         });
8709         
8710         return rowIndex;
8711     },
8712      /**
8713      * Returns the grid's underlying element = used by panel.Grid
8714      * @return {Element} The element
8715      */
8716     getGridEl : function(){
8717         return this.el;
8718     },
8719      /**
8720      * Forces a resize - used by panel.Grid
8721      * @return {Element} The element
8722      */
8723     autoSize : function()
8724     {
8725         //var ctr = Roo.get(this.container.dom.parentElement);
8726         var ctr = Roo.get(this.el.dom);
8727         
8728         var thd = this.getGridEl().select('thead',true).first();
8729         var tbd = this.getGridEl().select('tbody', true).first();
8730         var tfd = this.getGridEl().select('tfoot', true).first();
8731         
8732         var cw = ctr.getWidth();
8733         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8734         
8735         if (tbd) {
8736             
8737             tbd.setWidth(ctr.getWidth());
8738             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8739             // this needs fixing for various usage - currently only hydra job advers I think..
8740             //tdb.setHeight(
8741             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8742             //); 
8743             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8744             cw -= barsize;
8745         }
8746         cw = Math.max(cw, this.totalWidth);
8747         this.getGridEl().select('tbody tr',true).setWidth(cw);
8748         
8749         // resize 'expandable coloumn?
8750         
8751         return; // we doe not have a view in this design..
8752         
8753     },
8754     onBodyScroll: function()
8755     {
8756         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8757         if(this.mainHead){
8758             this.mainHead.setStyle({
8759                 'position' : 'relative',
8760                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8761             });
8762         }
8763         
8764         if(this.lazyLoad){
8765             
8766             var scrollHeight = this.mainBody.dom.scrollHeight;
8767             
8768             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8769             
8770             var height = this.mainBody.getHeight();
8771             
8772             if(scrollHeight - height == scrollTop) {
8773                 
8774                 var total = this.ds.getTotalCount();
8775                 
8776                 if(this.footer.cursor + this.footer.pageSize < total){
8777                     
8778                     this.footer.ds.load({
8779                         params : {
8780                             start : this.footer.cursor + this.footer.pageSize,
8781                             limit : this.footer.pageSize
8782                         },
8783                         add : true
8784                     });
8785                 }
8786             }
8787             
8788         }
8789     },
8790     
8791     onHeaderChange : function()
8792     {
8793         var header = this.renderHeader();
8794         var table = this.el.select('table', true).first();
8795         
8796         this.mainHead.remove();
8797         this.mainHead = table.createChild(header, this.mainBody, false);
8798     },
8799     
8800     onHiddenChange : function(colModel, colIndex, hidden)
8801     {
8802         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8803         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8804         
8805         this.CSS.updateRule(thSelector, "display", "");
8806         this.CSS.updateRule(tdSelector, "display", "");
8807         
8808         if(hidden){
8809             this.CSS.updateRule(thSelector, "display", "none");
8810             this.CSS.updateRule(tdSelector, "display", "none");
8811         }
8812         
8813         this.onHeaderChange();
8814         this.onLoad();
8815     },
8816     
8817     setColumnWidth: function(col_index, width)
8818     {
8819         // width = "md-2 xs-2..."
8820         if(!this.colModel.config[col_index]) {
8821             return;
8822         }
8823         
8824         var w = width.split(" ");
8825         
8826         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8827         
8828         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8829         
8830         
8831         for(var j = 0; j < w.length; j++) {
8832             
8833             if(!w[j]) {
8834                 continue;
8835             }
8836             
8837             var size_cls = w[j].split("-");
8838             
8839             if(!Number.isInteger(size_cls[1] * 1)) {
8840                 continue;
8841             }
8842             
8843             if(!this.colModel.config[col_index][size_cls[0]]) {
8844                 continue;
8845             }
8846             
8847             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8848                 continue;
8849             }
8850             
8851             h_row[0].classList.replace(
8852                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8853                 "col-"+size_cls[0]+"-"+size_cls[1]
8854             );
8855             
8856             for(var i = 0; i < rows.length; i++) {
8857                 
8858                 var size_cls = w[j].split("-");
8859                 
8860                 if(!Number.isInteger(size_cls[1] * 1)) {
8861                     continue;
8862                 }
8863                 
8864                 if(!this.colModel.config[col_index][size_cls[0]]) {
8865                     continue;
8866                 }
8867                 
8868                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8869                     continue;
8870                 }
8871                 
8872                 rows[i].classList.replace(
8873                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8874                     "col-"+size_cls[0]+"-"+size_cls[1]
8875                 );
8876             }
8877             
8878             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8879         }
8880     }
8881 });
8882
8883  
8884
8885  /*
8886  * - LGPL
8887  *
8888  * table cell
8889  * 
8890  */
8891
8892 /**
8893  * @class Roo.bootstrap.TableCell
8894  * @extends Roo.bootstrap.Component
8895  * Bootstrap TableCell class
8896  * @cfg {String} html cell contain text
8897  * @cfg {String} cls cell class
8898  * @cfg {String} tag cell tag (td|th) default td
8899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8900  * @cfg {String} align Aligns the content in a cell
8901  * @cfg {String} axis Categorizes cells
8902  * @cfg {String} bgcolor Specifies the background color of a cell
8903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8904  * @cfg {Number} colspan Specifies the number of columns a cell should span
8905  * @cfg {String} headers Specifies one or more header cells a cell is related to
8906  * @cfg {Number} height Sets the height of a cell
8907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8908  * @cfg {Number} rowspan Sets the number of rows a cell should span
8909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8910  * @cfg {String} valign Vertical aligns the content in a cell
8911  * @cfg {Number} width Specifies the width of a cell
8912  * 
8913  * @constructor
8914  * Create a new TableCell
8915  * @param {Object} config The config object
8916  */
8917
8918 Roo.bootstrap.TableCell = function(config){
8919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8920 };
8921
8922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8923     
8924     html: false,
8925     cls: false,
8926     tag: false,
8927     abbr: false,
8928     align: false,
8929     axis: false,
8930     bgcolor: false,
8931     charoff: false,
8932     colspan: false,
8933     headers: false,
8934     height: false,
8935     nowrap: false,
8936     rowspan: false,
8937     scope: false,
8938     valign: false,
8939     width: false,
8940     
8941     
8942     getAutoCreate : function(){
8943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8944         
8945         cfg = {
8946             tag: 'td'
8947         };
8948         
8949         if(this.tag){
8950             cfg.tag = this.tag;
8951         }
8952         
8953         if (this.html) {
8954             cfg.html=this.html
8955         }
8956         if (this.cls) {
8957             cfg.cls=this.cls
8958         }
8959         if (this.abbr) {
8960             cfg.abbr=this.abbr
8961         }
8962         if (this.align) {
8963             cfg.align=this.align
8964         }
8965         if (this.axis) {
8966             cfg.axis=this.axis
8967         }
8968         if (this.bgcolor) {
8969             cfg.bgcolor=this.bgcolor
8970         }
8971         if (this.charoff) {
8972             cfg.charoff=this.charoff
8973         }
8974         if (this.colspan) {
8975             cfg.colspan=this.colspan
8976         }
8977         if (this.headers) {
8978             cfg.headers=this.headers
8979         }
8980         if (this.height) {
8981             cfg.height=this.height
8982         }
8983         if (this.nowrap) {
8984             cfg.nowrap=this.nowrap
8985         }
8986         if (this.rowspan) {
8987             cfg.rowspan=this.rowspan
8988         }
8989         if (this.scope) {
8990             cfg.scope=this.scope
8991         }
8992         if (this.valign) {
8993             cfg.valign=this.valign
8994         }
8995         if (this.width) {
8996             cfg.width=this.width
8997         }
8998         
8999         
9000         return cfg;
9001     }
9002    
9003 });
9004
9005  
9006
9007  /*
9008  * - LGPL
9009  *
9010  * table row
9011  * 
9012  */
9013
9014 /**
9015  * @class Roo.bootstrap.TableRow
9016  * @extends Roo.bootstrap.Component
9017  * Bootstrap TableRow class
9018  * @cfg {String} cls row class
9019  * @cfg {String} align Aligns the content in a table row
9020  * @cfg {String} bgcolor Specifies a background color for a table row
9021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9022  * @cfg {String} valign Vertical aligns the content in a table row
9023  * 
9024  * @constructor
9025  * Create a new TableRow
9026  * @param {Object} config The config object
9027  */
9028
9029 Roo.bootstrap.TableRow = function(config){
9030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9031 };
9032
9033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9034     
9035     cls: false,
9036     align: false,
9037     bgcolor: false,
9038     charoff: false,
9039     valign: false,
9040     
9041     getAutoCreate : function(){
9042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9043         
9044         cfg = {
9045             tag: 'tr'
9046         };
9047             
9048         if(this.cls){
9049             cfg.cls = this.cls;
9050         }
9051         if(this.align){
9052             cfg.align = this.align;
9053         }
9054         if(this.bgcolor){
9055             cfg.bgcolor = this.bgcolor;
9056         }
9057         if(this.charoff){
9058             cfg.charoff = this.charoff;
9059         }
9060         if(this.valign){
9061             cfg.valign = this.valign;
9062         }
9063         
9064         return cfg;
9065     }
9066    
9067 });
9068
9069  
9070
9071  /*
9072  * - LGPL
9073  *
9074  * table body
9075  * 
9076  */
9077
9078 /**
9079  * @class Roo.bootstrap.TableBody
9080  * @extends Roo.bootstrap.Component
9081  * Bootstrap TableBody class
9082  * @cfg {String} cls element class
9083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9084  * @cfg {String} align Aligns the content inside the element
9085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9087  * 
9088  * @constructor
9089  * Create a new TableBody
9090  * @param {Object} config The config object
9091  */
9092
9093 Roo.bootstrap.TableBody = function(config){
9094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9095 };
9096
9097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9098     
9099     cls: false,
9100     tag: false,
9101     align: false,
9102     charoff: false,
9103     valign: false,
9104     
9105     getAutoCreate : function(){
9106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9107         
9108         cfg = {
9109             tag: 'tbody'
9110         };
9111             
9112         if (this.cls) {
9113             cfg.cls=this.cls
9114         }
9115         if(this.tag){
9116             cfg.tag = this.tag;
9117         }
9118         
9119         if(this.align){
9120             cfg.align = this.align;
9121         }
9122         if(this.charoff){
9123             cfg.charoff = this.charoff;
9124         }
9125         if(this.valign){
9126             cfg.valign = this.valign;
9127         }
9128         
9129         return cfg;
9130     }
9131     
9132     
9133 //    initEvents : function()
9134 //    {
9135 //        
9136 //        if(!this.store){
9137 //            return;
9138 //        }
9139 //        
9140 //        this.store = Roo.factory(this.store, Roo.data);
9141 //        this.store.on('load', this.onLoad, this);
9142 //        
9143 //        this.store.load();
9144 //        
9145 //    },
9146 //    
9147 //    onLoad: function () 
9148 //    {   
9149 //        this.fireEvent('load', this);
9150 //    }
9151 //    
9152 //   
9153 });
9154
9155  
9156
9157  /*
9158  * Based on:
9159  * Ext JS Library 1.1.1
9160  * Copyright(c) 2006-2007, Ext JS, LLC.
9161  *
9162  * Originally Released Under LGPL - original licence link has changed is not relivant.
9163  *
9164  * Fork - LGPL
9165  * <script type="text/javascript">
9166  */
9167
9168 // as we use this in bootstrap.
9169 Roo.namespace('Roo.form');
9170  /**
9171  * @class Roo.form.Action
9172  * Internal Class used to handle form actions
9173  * @constructor
9174  * @param {Roo.form.BasicForm} el The form element or its id
9175  * @param {Object} config Configuration options
9176  */
9177
9178  
9179  
9180 // define the action interface
9181 Roo.form.Action = function(form, options){
9182     this.form = form;
9183     this.options = options || {};
9184 };
9185 /**
9186  * Client Validation Failed
9187  * @const 
9188  */
9189 Roo.form.Action.CLIENT_INVALID = 'client';
9190 /**
9191  * Server Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.SERVER_INVALID = 'server';
9195  /**
9196  * Connect to Server Failed
9197  * @const 
9198  */
9199 Roo.form.Action.CONNECT_FAILURE = 'connect';
9200 /**
9201  * Reading Data from Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.LOAD_FAILURE = 'load';
9205
9206 Roo.form.Action.prototype = {
9207     type : 'default',
9208     failureType : undefined,
9209     response : undefined,
9210     result : undefined,
9211
9212     // interface method
9213     run : function(options){
9214
9215     },
9216
9217     // interface method
9218     success : function(response){
9219
9220     },
9221
9222     // interface method
9223     handleResponse : function(response){
9224
9225     },
9226
9227     // default connection failure
9228     failure : function(response){
9229         
9230         this.response = response;
9231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9232         this.form.afterAction(this, false);
9233     },
9234
9235     processResponse : function(response){
9236         this.response = response;
9237         if(!response.responseText){
9238             return true;
9239         }
9240         this.result = this.handleResponse(response);
9241         return this.result;
9242     },
9243
9244     // utility functions used internally
9245     getUrl : function(appendParams){
9246         var url = this.options.url || this.form.url || this.form.el.dom.action;
9247         if(appendParams){
9248             var p = this.getParams();
9249             if(p){
9250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9251             }
9252         }
9253         return url;
9254     },
9255
9256     getMethod : function(){
9257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9258     },
9259
9260     getParams : function(){
9261         var bp = this.form.baseParams;
9262         var p = this.options.params;
9263         if(p){
9264             if(typeof p == "object"){
9265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9266             }else if(typeof p == 'string' && bp){
9267                 p += '&' + Roo.urlEncode(bp);
9268             }
9269         }else if(bp){
9270             p = Roo.urlEncode(bp);
9271         }
9272         return p;
9273     },
9274
9275     createCallback : function(){
9276         return {
9277             success: this.success,
9278             failure: this.failure,
9279             scope: this,
9280             timeout: (this.form.timeout*1000),
9281             upload: this.form.fileUpload ? this.success : undefined
9282         };
9283     }
9284 };
9285
9286 Roo.form.Action.Submit = function(form, options){
9287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9288 };
9289
9290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9291     type : 'submit',
9292
9293     haveProgress : false,
9294     uploadComplete : false,
9295     
9296     // uploadProgress indicator.
9297     uploadProgress : function()
9298     {
9299         if (!this.form.progressUrl) {
9300             return;
9301         }
9302         
9303         if (!this.haveProgress) {
9304             Roo.MessageBox.progress("Uploading", "Uploading");
9305         }
9306         if (this.uploadComplete) {
9307            Roo.MessageBox.hide();
9308            return;
9309         }
9310         
9311         this.haveProgress = true;
9312    
9313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9314         
9315         var c = new Roo.data.Connection();
9316         c.request({
9317             url : this.form.progressUrl,
9318             params: {
9319                 id : uid
9320             },
9321             method: 'GET',
9322             success : function(req){
9323                //console.log(data);
9324                 var rdata = false;
9325                 var edata;
9326                 try  {
9327                    rdata = Roo.decode(req.responseText)
9328                 } catch (e) {
9329                     Roo.log("Invalid data from server..");
9330                     Roo.log(edata);
9331                     return;
9332                 }
9333                 if (!rdata || !rdata.success) {
9334                     Roo.log(rdata);
9335                     Roo.MessageBox.alert(Roo.encode(rdata));
9336                     return;
9337                 }
9338                 var data = rdata.data;
9339                 
9340                 if (this.uploadComplete) {
9341                    Roo.MessageBox.hide();
9342                    return;
9343                 }
9344                    
9345                 if (data){
9346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9348                     );
9349                 }
9350                 this.uploadProgress.defer(2000,this);
9351             },
9352        
9353             failure: function(data) {
9354                 Roo.log('progress url failed ');
9355                 Roo.log(data);
9356             },
9357             scope : this
9358         });
9359            
9360     },
9361     
9362     
9363     run : function()
9364     {
9365         // run get Values on the form, so it syncs any secondary forms.
9366         this.form.getValues();
9367         
9368         var o = this.options;
9369         var method = this.getMethod();
9370         var isPost = method == 'POST';
9371         if(o.clientValidation === false || this.form.isValid()){
9372             
9373             if (this.form.progressUrl) {
9374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9375                     (new Date() * 1) + '' + Math.random());
9376                     
9377             } 
9378             
9379             
9380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9381                 form:this.form.el.dom,
9382                 url:this.getUrl(!isPost),
9383                 method: method,
9384                 params:isPost ? this.getParams() : null,
9385                 isUpload: this.form.fileUpload,
9386                 formData : this.form.formData
9387             }));
9388             
9389             this.uploadProgress();
9390
9391         }else if (o.clientValidation !== false){ // client validation failed
9392             this.failureType = Roo.form.Action.CLIENT_INVALID;
9393             this.form.afterAction(this, false);
9394         }
9395     },
9396
9397     success : function(response)
9398     {
9399         this.uploadComplete= true;
9400         if (this.haveProgress) {
9401             Roo.MessageBox.hide();
9402         }
9403         
9404         
9405         var result = this.processResponse(response);
9406         if(result === true || result.success){
9407             this.form.afterAction(this, true);
9408             return;
9409         }
9410         if(result.errors){
9411             this.form.markInvalid(result.errors);
9412             this.failureType = Roo.form.Action.SERVER_INVALID;
9413         }
9414         this.form.afterAction(this, false);
9415     },
9416     failure : function(response)
9417     {
9418         this.uploadComplete= true;
9419         if (this.haveProgress) {
9420             Roo.MessageBox.hide();
9421         }
9422         
9423         this.response = response;
9424         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9425         this.form.afterAction(this, false);
9426     },
9427     
9428     handleResponse : function(response){
9429         if(this.form.errorReader){
9430             var rs = this.form.errorReader.read(response);
9431             var errors = [];
9432             if(rs.records){
9433                 for(var i = 0, len = rs.records.length; i < len; i++) {
9434                     var r = rs.records[i];
9435                     errors[i] = r.data;
9436                 }
9437             }
9438             if(errors.length < 1){
9439                 errors = null;
9440             }
9441             return {
9442                 success : rs.success,
9443                 errors : errors
9444             };
9445         }
9446         var ret = false;
9447         try {
9448             ret = Roo.decode(response.responseText);
9449         } catch (e) {
9450             ret = {
9451                 success: false,
9452                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9453                 errors : []
9454             };
9455         }
9456         return ret;
9457         
9458     }
9459 });
9460
9461
9462 Roo.form.Action.Load = function(form, options){
9463     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9464     this.reader = this.form.reader;
9465 };
9466
9467 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9468     type : 'load',
9469
9470     run : function(){
9471         
9472         Roo.Ajax.request(Roo.apply(
9473                 this.createCallback(), {
9474                     method:this.getMethod(),
9475                     url:this.getUrl(false),
9476                     params:this.getParams()
9477         }));
9478     },
9479
9480     success : function(response){
9481         
9482         var result = this.processResponse(response);
9483         if(result === true || !result.success || !result.data){
9484             this.failureType = Roo.form.Action.LOAD_FAILURE;
9485             this.form.afterAction(this, false);
9486             return;
9487         }
9488         this.form.clearInvalid();
9489         this.form.setValues(result.data);
9490         this.form.afterAction(this, true);
9491     },
9492
9493     handleResponse : function(response){
9494         if(this.form.reader){
9495             var rs = this.form.reader.read(response);
9496             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9497             return {
9498                 success : rs.success,
9499                 data : data
9500             };
9501         }
9502         return Roo.decode(response.responseText);
9503     }
9504 });
9505
9506 Roo.form.Action.ACTION_TYPES = {
9507     'load' : Roo.form.Action.Load,
9508     'submit' : Roo.form.Action.Submit
9509 };/*
9510  * - LGPL
9511  *
9512  * form
9513  *
9514  */
9515
9516 /**
9517  * @class Roo.bootstrap.Form
9518  * @extends Roo.bootstrap.Component
9519  * Bootstrap Form class
9520  * @cfg {String} method  GET | POST (default POST)
9521  * @cfg {String} labelAlign top | left (default top)
9522  * @cfg {String} align left  | right - for navbars
9523  * @cfg {Boolean} loadMask load mask when submit (default true)
9524
9525  *
9526  * @constructor
9527  * Create a new Form
9528  * @param {Object} config The config object
9529  */
9530
9531
9532 Roo.bootstrap.Form = function(config){
9533     
9534     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9535     
9536     Roo.bootstrap.Form.popover.apply();
9537     
9538     this.addEvents({
9539         /**
9540          * @event clientvalidation
9541          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9542          * @param {Form} this
9543          * @param {Boolean} valid true if the form has passed client-side validation
9544          */
9545         clientvalidation: true,
9546         /**
9547          * @event beforeaction
9548          * Fires before any action is performed. Return false to cancel the action.
9549          * @param {Form} this
9550          * @param {Action} action The action to be performed
9551          */
9552         beforeaction: true,
9553         /**
9554          * @event actionfailed
9555          * Fires when an action fails.
9556          * @param {Form} this
9557          * @param {Action} action The action that failed
9558          */
9559         actionfailed : true,
9560         /**
9561          * @event actioncomplete
9562          * Fires when an action is completed.
9563          * @param {Form} this
9564          * @param {Action} action The action that completed
9565          */
9566         actioncomplete : true
9567     });
9568 };
9569
9570 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9571
9572      /**
9573      * @cfg {String} method
9574      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9575      */
9576     method : 'POST',
9577     /**
9578      * @cfg {String} url
9579      * The URL to use for form actions if one isn't supplied in the action options.
9580      */
9581     /**
9582      * @cfg {Boolean} fileUpload
9583      * Set to true if this form is a file upload.
9584      */
9585
9586     /**
9587      * @cfg {Object} baseParams
9588      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9589      */
9590
9591     /**
9592      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9593      */
9594     timeout: 30,
9595     /**
9596      * @cfg {Sting} align (left|right) for navbar forms
9597      */
9598     align : 'left',
9599
9600     // private
9601     activeAction : null,
9602
9603     /**
9604      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9605      * element by passing it or its id or mask the form itself by passing in true.
9606      * @type Mixed
9607      */
9608     waitMsgTarget : false,
9609
9610     loadMask : true,
9611     
9612     /**
9613      * @cfg {Boolean} errorMask (true|false) default false
9614      */
9615     errorMask : false,
9616     
9617     /**
9618      * @cfg {Number} maskOffset Default 100
9619      */
9620     maskOffset : 100,
9621     
9622     /**
9623      * @cfg {Boolean} maskBody
9624      */
9625     maskBody : false,
9626
9627     getAutoCreate : function(){
9628
9629         var cfg = {
9630             tag: 'form',
9631             method : this.method || 'POST',
9632             id : this.id || Roo.id(),
9633             cls : ''
9634         };
9635         if (this.parent().xtype.match(/^Nav/)) {
9636             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9637
9638         }
9639
9640         if (this.labelAlign == 'left' ) {
9641             cfg.cls += ' form-horizontal';
9642         }
9643
9644
9645         return cfg;
9646     },
9647     initEvents : function()
9648     {
9649         this.el.on('submit', this.onSubmit, this);
9650         // this was added as random key presses on the form where triggering form submit.
9651         this.el.on('keypress', function(e) {
9652             if (e.getCharCode() != 13) {
9653                 return true;
9654             }
9655             // we might need to allow it for textareas.. and some other items.
9656             // check e.getTarget().
9657
9658             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9659                 return true;
9660             }
9661
9662             Roo.log("keypress blocked");
9663
9664             e.preventDefault();
9665             return false;
9666         });
9667         
9668     },
9669     // private
9670     onSubmit : function(e){
9671         e.stopEvent();
9672     },
9673
9674      /**
9675      * Returns true if client-side validation on the form is successful.
9676      * @return Boolean
9677      */
9678     isValid : function(){
9679         var items = this.getItems();
9680         var valid = true;
9681         var target = false;
9682         
9683         items.each(function(f){
9684             
9685             if(f.validate()){
9686                 return;
9687             }
9688             
9689             Roo.log('invalid field: ' + f.name);
9690             
9691             valid = false;
9692
9693             if(!target && f.el.isVisible(true)){
9694                 target = f;
9695             }
9696            
9697         });
9698         
9699         if(this.errorMask && !valid){
9700             Roo.bootstrap.Form.popover.mask(this, target);
9701         }
9702         
9703         return valid;
9704     },
9705     
9706     /**
9707      * Returns true if any fields in this form have changed since their original load.
9708      * @return Boolean
9709      */
9710     isDirty : function(){
9711         var dirty = false;
9712         var items = this.getItems();
9713         items.each(function(f){
9714            if(f.isDirty()){
9715                dirty = true;
9716                return false;
9717            }
9718            return true;
9719         });
9720         return dirty;
9721     },
9722      /**
9723      * Performs a predefined action (submit or load) or custom actions you define on this form.
9724      * @param {String} actionName The name of the action type
9725      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9726      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9727      * accept other config options):
9728      * <pre>
9729 Property          Type             Description
9730 ----------------  ---------------  ----------------------------------------------------------------------------------
9731 url               String           The url for the action (defaults to the form's url)
9732 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9733 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9734 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9735                                    validate the form on the client (defaults to false)
9736      * </pre>
9737      * @return {BasicForm} this
9738      */
9739     doAction : function(action, options){
9740         if(typeof action == 'string'){
9741             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9742         }
9743         if(this.fireEvent('beforeaction', this, action) !== false){
9744             this.beforeAction(action);
9745             action.run.defer(100, action);
9746         }
9747         return this;
9748     },
9749
9750     // private
9751     beforeAction : function(action){
9752         var o = action.options;
9753         
9754         if(this.loadMask){
9755             
9756             if(this.maskBody){
9757                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9758             } else {
9759                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9760             }
9761         }
9762         // not really supported yet.. ??
9763
9764         //if(this.waitMsgTarget === true){
9765         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9766         //}else if(this.waitMsgTarget){
9767         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9768         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9769         //}else {
9770         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9771        // }
9772
9773     },
9774
9775     // private
9776     afterAction : function(action, success){
9777         this.activeAction = null;
9778         var o = action.options;
9779
9780         if(this.loadMask){
9781             
9782             if(this.maskBody){
9783                 Roo.get(document.body).unmask();
9784             } else {
9785                 this.el.unmask();
9786             }
9787         }
9788         
9789         //if(this.waitMsgTarget === true){
9790 //            this.el.unmask();
9791         //}else if(this.waitMsgTarget){
9792         //    this.waitMsgTarget.unmask();
9793         //}else{
9794         //    Roo.MessageBox.updateProgress(1);
9795         //    Roo.MessageBox.hide();
9796        // }
9797         //
9798         if(success){
9799             if(o.reset){
9800                 this.reset();
9801             }
9802             Roo.callback(o.success, o.scope, [this, action]);
9803             this.fireEvent('actioncomplete', this, action);
9804
9805         }else{
9806
9807             // failure condition..
9808             // we have a scenario where updates need confirming.
9809             // eg. if a locking scenario exists..
9810             // we look for { errors : { needs_confirm : true }} in the response.
9811             if (
9812                 (typeof(action.result) != 'undefined')  &&
9813                 (typeof(action.result.errors) != 'undefined')  &&
9814                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9815            ){
9816                 var _t = this;
9817                 Roo.log("not supported yet");
9818                  /*
9819
9820                 Roo.MessageBox.confirm(
9821                     "Change requires confirmation",
9822                     action.result.errorMsg,
9823                     function(r) {
9824                         if (r != 'yes') {
9825                             return;
9826                         }
9827                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9828                     }
9829
9830                 );
9831                 */
9832
9833
9834                 return;
9835             }
9836
9837             Roo.callback(o.failure, o.scope, [this, action]);
9838             // show an error message if no failed handler is set..
9839             if (!this.hasListener('actionfailed')) {
9840                 Roo.log("need to add dialog support");
9841                 /*
9842                 Roo.MessageBox.alert("Error",
9843                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9844                         action.result.errorMsg :
9845                         "Saving Failed, please check your entries or try again"
9846                 );
9847                 */
9848             }
9849
9850             this.fireEvent('actionfailed', this, action);
9851         }
9852
9853     },
9854     /**
9855      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9856      * @param {String} id The value to search for
9857      * @return Field
9858      */
9859     findField : function(id){
9860         var items = this.getItems();
9861         var field = items.get(id);
9862         if(!field){
9863              items.each(function(f){
9864                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9865                     field = f;
9866                     return false;
9867                 }
9868                 return true;
9869             });
9870         }
9871         return field || null;
9872     },
9873      /**
9874      * Mark fields in this form invalid in bulk.
9875      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9876      * @return {BasicForm} this
9877      */
9878     markInvalid : function(errors){
9879         if(errors instanceof Array){
9880             for(var i = 0, len = errors.length; i < len; i++){
9881                 var fieldError = errors[i];
9882                 var f = this.findField(fieldError.id);
9883                 if(f){
9884                     f.markInvalid(fieldError.msg);
9885                 }
9886             }
9887         }else{
9888             var field, id;
9889             for(id in errors){
9890                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9891                     field.markInvalid(errors[id]);
9892                 }
9893             }
9894         }
9895         //Roo.each(this.childForms || [], function (f) {
9896         //    f.markInvalid(errors);
9897         //});
9898
9899         return this;
9900     },
9901
9902     /**
9903      * Set values for fields in this form in bulk.
9904      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9905      * @return {BasicForm} this
9906      */
9907     setValues : function(values){
9908         if(values instanceof Array){ // array of objects
9909             for(var i = 0, len = values.length; i < len; i++){
9910                 var v = values[i];
9911                 var f = this.findField(v.id);
9912                 if(f){
9913                     f.setValue(v.value);
9914                     if(this.trackResetOnLoad){
9915                         f.originalValue = f.getValue();
9916                     }
9917                 }
9918             }
9919         }else{ // object hash
9920             var field, id;
9921             for(id in values){
9922                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9923
9924                     if (field.setFromData &&
9925                         field.valueField &&
9926                         field.displayField &&
9927                         // combos' with local stores can
9928                         // be queried via setValue()
9929                         // to set their value..
9930                         (field.store && !field.store.isLocal)
9931                         ) {
9932                         // it's a combo
9933                         var sd = { };
9934                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9935                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9936                         field.setFromData(sd);
9937
9938                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9939                         
9940                         field.setFromData(values);
9941                         
9942                     } else {
9943                         field.setValue(values[id]);
9944                     }
9945
9946
9947                     if(this.trackResetOnLoad){
9948                         field.originalValue = field.getValue();
9949                     }
9950                 }
9951             }
9952         }
9953
9954         //Roo.each(this.childForms || [], function (f) {
9955         //    f.setValues(values);
9956         //});
9957
9958         return this;
9959     },
9960
9961     /**
9962      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9963      * they are returned as an array.
9964      * @param {Boolean} asString
9965      * @return {Object}
9966      */
9967     getValues : function(asString){
9968         //if (this.childForms) {
9969             // copy values from the child forms
9970         //    Roo.each(this.childForms, function (f) {
9971         //        this.setValues(f.getValues());
9972         //    }, this);
9973         //}
9974
9975
9976
9977         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9978         if(asString === true){
9979             return fs;
9980         }
9981         return Roo.urlDecode(fs);
9982     },
9983
9984     /**
9985      * Returns the fields in this form as an object with key/value pairs.
9986      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9987      * @return {Object}
9988      */
9989     getFieldValues : function(with_hidden)
9990     {
9991         var items = this.getItems();
9992         var ret = {};
9993         items.each(function(f){
9994             
9995             if (!f.getName()) {
9996                 return;
9997             }
9998             
9999             var v = f.getValue();
10000             
10001             if (f.inputType =='radio') {
10002                 if (typeof(ret[f.getName()]) == 'undefined') {
10003                     ret[f.getName()] = ''; // empty..
10004                 }
10005
10006                 if (!f.el.dom.checked) {
10007                     return;
10008
10009                 }
10010                 v = f.el.dom.value;
10011
10012             }
10013             
10014             if(f.xtype == 'MoneyField'){
10015                 ret[f.currencyName] = f.getCurrency();
10016             }
10017
10018             // not sure if this supported any more..
10019             if ((typeof(v) == 'object') && f.getRawValue) {
10020                 v = f.getRawValue() ; // dates..
10021             }
10022             // combo boxes where name != hiddenName...
10023             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10024                 ret[f.name] = f.getRawValue();
10025             }
10026             ret[f.getName()] = v;
10027         });
10028
10029         return ret;
10030     },
10031
10032     /**
10033      * Clears all invalid messages in this form.
10034      * @return {BasicForm} this
10035      */
10036     clearInvalid : function(){
10037         var items = this.getItems();
10038
10039         items.each(function(f){
10040            f.clearInvalid();
10041         });
10042
10043         return this;
10044     },
10045
10046     /**
10047      * Resets this form.
10048      * @return {BasicForm} this
10049      */
10050     reset : function(){
10051         var items = this.getItems();
10052         items.each(function(f){
10053             f.reset();
10054         });
10055
10056         Roo.each(this.childForms || [], function (f) {
10057             f.reset();
10058         });
10059
10060
10061         return this;
10062     },
10063     
10064     getItems : function()
10065     {
10066         var r=new Roo.util.MixedCollection(false, function(o){
10067             return o.id || (o.id = Roo.id());
10068         });
10069         var iter = function(el) {
10070             if (el.inputEl) {
10071                 r.add(el);
10072             }
10073             if (!el.items) {
10074                 return;
10075             }
10076             Roo.each(el.items,function(e) {
10077                 iter(e);
10078             });
10079         };
10080
10081         iter(this);
10082         return r;
10083     },
10084     
10085     hideFields : function(items)
10086     {
10087         Roo.each(items, function(i){
10088             
10089             var f = this.findField(i);
10090             
10091             if(!f){
10092                 return;
10093             }
10094             
10095             f.hide();
10096             
10097         }, this);
10098     },
10099     
10100     showFields : function(items)
10101     {
10102         Roo.each(items, function(i){
10103             
10104             var f = this.findField(i);
10105             
10106             if(!f){
10107                 return;
10108             }
10109             
10110             f.show();
10111             
10112         }, this);
10113     }
10114
10115 });
10116
10117 Roo.apply(Roo.bootstrap.Form, {
10118     
10119     popover : {
10120         
10121         padding : 5,
10122         
10123         isApplied : false,
10124         
10125         isMasked : false,
10126         
10127         form : false,
10128         
10129         target : false,
10130         
10131         toolTip : false,
10132         
10133         intervalID : false,
10134         
10135         maskEl : false,
10136         
10137         apply : function()
10138         {
10139             if(this.isApplied){
10140                 return;
10141             }
10142             
10143             this.maskEl = {
10144                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10145                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10146                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10147                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10148             };
10149             
10150             this.maskEl.top.enableDisplayMode("block");
10151             this.maskEl.left.enableDisplayMode("block");
10152             this.maskEl.bottom.enableDisplayMode("block");
10153             this.maskEl.right.enableDisplayMode("block");
10154             
10155             this.toolTip = new Roo.bootstrap.Tooltip({
10156                 cls : 'roo-form-error-popover',
10157                 alignment : {
10158                     'left' : ['r-l', [-2,0], 'right'],
10159                     'right' : ['l-r', [2,0], 'left'],
10160                     'bottom' : ['tl-bl', [0,2], 'top'],
10161                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10162                 }
10163             });
10164             
10165             this.toolTip.render(Roo.get(document.body));
10166
10167             this.toolTip.el.enableDisplayMode("block");
10168             
10169             Roo.get(document.body).on('click', function(){
10170                 this.unmask();
10171             }, this);
10172             
10173             Roo.get(document.body).on('touchstart', function(){
10174                 this.unmask();
10175             }, this);
10176             
10177             this.isApplied = true
10178         },
10179         
10180         mask : function(form, target)
10181         {
10182             this.form = form;
10183             
10184             this.target = target;
10185             
10186             if(!this.form.errorMask || !target.el){
10187                 return;
10188             }
10189             
10190             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10191             
10192             Roo.log(scrollable);
10193             
10194             var ot = this.target.el.calcOffsetsTo(scrollable);
10195             
10196             var scrollTo = ot[1] - this.form.maskOffset;
10197             
10198             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10199             
10200             scrollable.scrollTo('top', scrollTo);
10201             
10202             var box = this.target.el.getBox();
10203             Roo.log(box);
10204             var zIndex = Roo.bootstrap.Modal.zIndex++;
10205
10206             
10207             this.maskEl.top.setStyle('position', 'absolute');
10208             this.maskEl.top.setStyle('z-index', zIndex);
10209             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10210             this.maskEl.top.setLeft(0);
10211             this.maskEl.top.setTop(0);
10212             this.maskEl.top.show();
10213             
10214             this.maskEl.left.setStyle('position', 'absolute');
10215             this.maskEl.left.setStyle('z-index', zIndex);
10216             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10217             this.maskEl.left.setLeft(0);
10218             this.maskEl.left.setTop(box.y - this.padding);
10219             this.maskEl.left.show();
10220
10221             this.maskEl.bottom.setStyle('position', 'absolute');
10222             this.maskEl.bottom.setStyle('z-index', zIndex);
10223             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10224             this.maskEl.bottom.setLeft(0);
10225             this.maskEl.bottom.setTop(box.bottom + this.padding);
10226             this.maskEl.bottom.show();
10227
10228             this.maskEl.right.setStyle('position', 'absolute');
10229             this.maskEl.right.setStyle('z-index', zIndex);
10230             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10231             this.maskEl.right.setLeft(box.right + this.padding);
10232             this.maskEl.right.setTop(box.y - this.padding);
10233             this.maskEl.right.show();
10234
10235             this.toolTip.bindEl = this.target.el;
10236
10237             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10238
10239             var tip = this.target.blankText;
10240
10241             if(this.target.getValue() !== '' ) {
10242                 
10243                 if (this.target.invalidText.length) {
10244                     tip = this.target.invalidText;
10245                 } else if (this.target.regexText.length){
10246                     tip = this.target.regexText;
10247                 }
10248             }
10249
10250             this.toolTip.show(tip);
10251
10252             this.intervalID = window.setInterval(function() {
10253                 Roo.bootstrap.Form.popover.unmask();
10254             }, 10000);
10255
10256             window.onwheel = function(){ return false;};
10257             
10258             (function(){ this.isMasked = true; }).defer(500, this);
10259             
10260         },
10261         
10262         unmask : function()
10263         {
10264             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10265                 return;
10266             }
10267             
10268             this.maskEl.top.setStyle('position', 'absolute');
10269             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10270             this.maskEl.top.hide();
10271
10272             this.maskEl.left.setStyle('position', 'absolute');
10273             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10274             this.maskEl.left.hide();
10275
10276             this.maskEl.bottom.setStyle('position', 'absolute');
10277             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.bottom.hide();
10279
10280             this.maskEl.right.setStyle('position', 'absolute');
10281             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.right.hide();
10283             
10284             this.toolTip.hide();
10285             
10286             this.toolTip.el.hide();
10287             
10288             window.onwheel = function(){ return true;};
10289             
10290             if(this.intervalID){
10291                 window.clearInterval(this.intervalID);
10292                 this.intervalID = false;
10293             }
10294             
10295             this.isMasked = false;
10296             
10297         }
10298         
10299     }
10300     
10301 });
10302
10303 /*
10304  * Based on:
10305  * Ext JS Library 1.1.1
10306  * Copyright(c) 2006-2007, Ext JS, LLC.
10307  *
10308  * Originally Released Under LGPL - original licence link has changed is not relivant.
10309  *
10310  * Fork - LGPL
10311  * <script type="text/javascript">
10312  */
10313 /**
10314  * @class Roo.form.VTypes
10315  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10316  * @singleton
10317  */
10318 Roo.form.VTypes = function(){
10319     // closure these in so they are only created once.
10320     var alpha = /^[a-zA-Z_]+$/;
10321     var alphanum = /^[a-zA-Z0-9_]+$/;
10322     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10323     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10324
10325     // All these messages and functions are configurable
10326     return {
10327         /**
10328          * The function used to validate email addresses
10329          * @param {String} value The email address
10330          */
10331         'email' : function(v){
10332             return email.test(v);
10333         },
10334         /**
10335          * The error text to display when the email validation function returns false
10336          * @type String
10337          */
10338         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10339         /**
10340          * The keystroke filter mask to be applied on email input
10341          * @type RegExp
10342          */
10343         'emailMask' : /[a-z0-9_\.\-@]/i,
10344
10345         /**
10346          * The function used to validate URLs
10347          * @param {String} value The URL
10348          */
10349         'url' : function(v){
10350             return url.test(v);
10351         },
10352         /**
10353          * The error text to display when the url validation function returns false
10354          * @type String
10355          */
10356         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10357         
10358         /**
10359          * The function used to validate alpha values
10360          * @param {String} value The value
10361          */
10362         'alpha' : function(v){
10363             return alpha.test(v);
10364         },
10365         /**
10366          * The error text to display when the alpha validation function returns false
10367          * @type String
10368          */
10369         'alphaText' : 'This field should only contain letters and _',
10370         /**
10371          * The keystroke filter mask to be applied on alpha input
10372          * @type RegExp
10373          */
10374         'alphaMask' : /[a-z_]/i,
10375
10376         /**
10377          * The function used to validate alphanumeric values
10378          * @param {String} value The value
10379          */
10380         'alphanum' : function(v){
10381             return alphanum.test(v);
10382         },
10383         /**
10384          * The error text to display when the alphanumeric validation function returns false
10385          * @type String
10386          */
10387         'alphanumText' : 'This field should only contain letters, numbers and _',
10388         /**
10389          * The keystroke filter mask to be applied on alphanumeric input
10390          * @type RegExp
10391          */
10392         'alphanumMask' : /[a-z0-9_]/i
10393     };
10394 }();/*
10395  * - LGPL
10396  *
10397  * Input
10398  * 
10399  */
10400
10401 /**
10402  * @class Roo.bootstrap.Input
10403  * @extends Roo.bootstrap.Component
10404  * Bootstrap Input class
10405  * @cfg {Boolean} disabled is it disabled
10406  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10407  * @cfg {String} name name of the input
10408  * @cfg {string} fieldLabel - the label associated
10409  * @cfg {string} placeholder - placeholder to put in text.
10410  * @cfg {string}  before - input group add on before
10411  * @cfg {string} after - input group add on after
10412  * @cfg {string} size - (lg|sm) or leave empty..
10413  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10414  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10415  * @cfg {Number} md colspan out of 12 for computer-sized screens
10416  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10417  * @cfg {string} value default value of the input
10418  * @cfg {Number} labelWidth set the width of label 
10419  * @cfg {Number} labellg set the width of label (1-12)
10420  * @cfg {Number} labelmd set the width of label (1-12)
10421  * @cfg {Number} labelsm set the width of label (1-12)
10422  * @cfg {Number} labelxs set the width of label (1-12)
10423  * @cfg {String} labelAlign (top|left)
10424  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10425  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10426  * @cfg {String} indicatorpos (left|right) default left
10427  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10428  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10429  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10430
10431  * @cfg {String} align (left|center|right) Default left
10432  * @cfg {Boolean} forceFeedback (true|false) Default false
10433  * 
10434  * @constructor
10435  * Create a new Input
10436  * @param {Object} config The config object
10437  */
10438
10439 Roo.bootstrap.Input = function(config){
10440     
10441     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10442     
10443     this.addEvents({
10444         /**
10445          * @event focus
10446          * Fires when this field receives input focus.
10447          * @param {Roo.form.Field} this
10448          */
10449         focus : true,
10450         /**
10451          * @event blur
10452          * Fires when this field loses input focus.
10453          * @param {Roo.form.Field} this
10454          */
10455         blur : true,
10456         /**
10457          * @event specialkey
10458          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10459          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10460          * @param {Roo.form.Field} this
10461          * @param {Roo.EventObject} e The event object
10462          */
10463         specialkey : true,
10464         /**
10465          * @event change
10466          * Fires just before the field blurs if the field value has changed.
10467          * @param {Roo.form.Field} this
10468          * @param {Mixed} newValue The new value
10469          * @param {Mixed} oldValue The original value
10470          */
10471         change : true,
10472         /**
10473          * @event invalid
10474          * Fires after the field has been marked as invalid.
10475          * @param {Roo.form.Field} this
10476          * @param {String} msg The validation message
10477          */
10478         invalid : true,
10479         /**
10480          * @event valid
10481          * Fires after the field has been validated with no errors.
10482          * @param {Roo.form.Field} this
10483          */
10484         valid : true,
10485          /**
10486          * @event keyup
10487          * Fires after the key up
10488          * @param {Roo.form.Field} this
10489          * @param {Roo.EventObject}  e The event Object
10490          */
10491         keyup : true
10492     });
10493 };
10494
10495 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10496      /**
10497      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10498       automatic validation (defaults to "keyup").
10499      */
10500     validationEvent : "keyup",
10501      /**
10502      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10503      */
10504     validateOnBlur : true,
10505     /**
10506      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10507      */
10508     validationDelay : 250,
10509      /**
10510      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10511      */
10512     focusClass : "x-form-focus",  // not needed???
10513     
10514        
10515     /**
10516      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10517      */
10518     invalidClass : "has-warning",
10519     
10520     /**
10521      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     validClass : "has-success",
10524     
10525     /**
10526      * @cfg {Boolean} hasFeedback (true|false) default true
10527      */
10528     hasFeedback : true,
10529     
10530     /**
10531      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10532      */
10533     invalidFeedbackClass : "glyphicon-warning-sign",
10534     
10535     /**
10536      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     validFeedbackClass : "glyphicon-ok",
10539     
10540     /**
10541      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10542      */
10543     selectOnFocus : false,
10544     
10545      /**
10546      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10547      */
10548     maskRe : null,
10549        /**
10550      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10551      */
10552     vtype : null,
10553     
10554       /**
10555      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10556      */
10557     disableKeyFilter : false,
10558     
10559        /**
10560      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10561      */
10562     disabled : false,
10563      /**
10564      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10565      */
10566     allowBlank : true,
10567     /**
10568      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10569      */
10570     blankText : "Please complete this mandatory field",
10571     
10572      /**
10573      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10574      */
10575     minLength : 0,
10576     /**
10577      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10578      */
10579     maxLength : Number.MAX_VALUE,
10580     /**
10581      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10582      */
10583     minLengthText : "The minimum length for this field is {0}",
10584     /**
10585      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10586      */
10587     maxLengthText : "The maximum length for this field is {0}",
10588   
10589     
10590     /**
10591      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10592      * If available, this function will be called only after the basic validators all return true, and will be passed the
10593      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10594      */
10595     validator : null,
10596     /**
10597      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10598      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10599      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10600      */
10601     regex : null,
10602     /**
10603      * @cfg {String} regexText -- Depricated - use Invalid Text
10604      */
10605     regexText : "",
10606     
10607     /**
10608      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10609      */
10610     invalidText : "",
10611     
10612     
10613     
10614     autocomplete: false,
10615     
10616     
10617     fieldLabel : '',
10618     inputType : 'text',
10619     
10620     name : false,
10621     placeholder: false,
10622     before : false,
10623     after : false,
10624     size : false,
10625     hasFocus : false,
10626     preventMark: false,
10627     isFormField : true,
10628     value : '',
10629     labelWidth : 2,
10630     labelAlign : false,
10631     readOnly : false,
10632     align : false,
10633     formatedValue : false,
10634     forceFeedback : false,
10635     
10636     indicatorpos : 'left',
10637     
10638     labellg : 0,
10639     labelmd : 0,
10640     labelsm : 0,
10641     labelxs : 0,
10642     
10643     capture : '',
10644     accept : '',
10645     
10646     parentLabelAlign : function()
10647     {
10648         var parent = this;
10649         while (parent.parent()) {
10650             parent = parent.parent();
10651             if (typeof(parent.labelAlign) !='undefined') {
10652                 return parent.labelAlign;
10653             }
10654         }
10655         return 'left';
10656         
10657     },
10658     
10659     getAutoCreate : function()
10660     {
10661         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10662         
10663         var id = Roo.id();
10664         
10665         var cfg = {};
10666         
10667         if(this.inputType != 'hidden'){
10668             cfg.cls = 'form-group' //input-group
10669         }
10670         
10671         var input =  {
10672             tag: 'input',
10673             id : id,
10674             type : this.inputType,
10675             value : this.value,
10676             cls : 'form-control',
10677             placeholder : this.placeholder || '',
10678             autocomplete : this.autocomplete || 'new-password'
10679         };
10680         if (this.inputType == 'file') {
10681             input.style = 'overflow:hidden'; // why not in CSS?
10682         }
10683         
10684         if(this.capture.length){
10685             input.capture = this.capture;
10686         }
10687         
10688         if(this.accept.length){
10689             input.accept = this.accept + "/*";
10690         }
10691         
10692         if(this.align){
10693             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10694         }
10695         
10696         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10697             input.maxLength = this.maxLength;
10698         }
10699         
10700         if (this.disabled) {
10701             input.disabled=true;
10702         }
10703         
10704         if (this.readOnly) {
10705             input.readonly=true;
10706         }
10707         
10708         if (this.name) {
10709             input.name = this.name;
10710         }
10711         
10712         if (this.size) {
10713             input.cls += ' input-' + this.size;
10714         }
10715         
10716         var settings=this;
10717         ['xs','sm','md','lg'].map(function(size){
10718             if (settings[size]) {
10719                 cfg.cls += ' col-' + size + '-' + settings[size];
10720             }
10721         });
10722         
10723         var inputblock = input;
10724         
10725         var feedback = {
10726             tag: 'span',
10727             cls: 'glyphicon form-control-feedback'
10728         };
10729             
10730         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10731             
10732             inputblock = {
10733                 cls : 'has-feedback',
10734                 cn :  [
10735                     input,
10736                     feedback
10737                 ] 
10738             };  
10739         }
10740         
10741         if (this.before || this.after) {
10742             
10743             inputblock = {
10744                 cls : 'input-group',
10745                 cn :  [] 
10746             };
10747             
10748             if (this.before && typeof(this.before) == 'string') {
10749                 
10750                 inputblock.cn.push({
10751                     tag :'span',
10752                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10753                     html : this.before
10754                 });
10755             }
10756             if (this.before && typeof(this.before) == 'object') {
10757                 this.before = Roo.factory(this.before);
10758                 
10759                 inputblock.cn.push({
10760                     tag :'span',
10761                     cls : 'roo-input-before input-group-prepend   input-group-' +
10762                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10763                 });
10764             }
10765             
10766             inputblock.cn.push(input);
10767             
10768             if (this.after && typeof(this.after) == 'string') {
10769                 inputblock.cn.push({
10770                     tag :'span',
10771                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10772                     html : this.after
10773                 });
10774             }
10775             if (this.after && typeof(this.after) == 'object') {
10776                 this.after = Roo.factory(this.after);
10777                 
10778                 inputblock.cn.push({
10779                     tag :'span',
10780                     cls : 'roo-input-after input-group-append  input-group-' +
10781                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10782                 });
10783             }
10784             
10785             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10786                 inputblock.cls += ' has-feedback';
10787                 inputblock.cn.push(feedback);
10788             }
10789         };
10790         var indicator = {
10791             tag : 'i',
10792             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10793             tooltip : 'This field is required'
10794         };
10795         if (this.allowBlank ) {
10796             indicator.style = this.allowBlank ? ' display:none' : '';
10797         }
10798         if (align ==='left' && this.fieldLabel.length) {
10799             
10800             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10801             
10802             cfg.cn = [
10803                 indicator,
10804                 {
10805                     tag: 'label',
10806                     'for' :  id,
10807                     cls : 'control-label col-form-label',
10808                     html : this.fieldLabel
10809
10810                 },
10811                 {
10812                     cls : "", 
10813                     cn: [
10814                         inputblock
10815                     ]
10816                 }
10817             ];
10818             
10819             var labelCfg = cfg.cn[1];
10820             var contentCfg = cfg.cn[2];
10821             
10822             if(this.indicatorpos == 'right'){
10823                 cfg.cn = [
10824                     {
10825                         tag: 'label',
10826                         'for' :  id,
10827                         cls : 'control-label col-form-label',
10828                         cn : [
10829                             {
10830                                 tag : 'span',
10831                                 html : this.fieldLabel
10832                             },
10833                             indicator
10834                         ]
10835                     },
10836                     {
10837                         cls : "",
10838                         cn: [
10839                             inputblock
10840                         ]
10841                     }
10842
10843                 ];
10844                 
10845                 labelCfg = cfg.cn[0];
10846                 contentCfg = cfg.cn[1];
10847             
10848             }
10849             
10850             if(this.labelWidth > 12){
10851                 labelCfg.style = "width: " + this.labelWidth + 'px';
10852             }
10853             
10854             if(this.labelWidth < 13 && this.labelmd == 0){
10855                 this.labelmd = this.labelWidth;
10856             }
10857             
10858             if(this.labellg > 0){
10859                 labelCfg.cls += ' col-lg-' + this.labellg;
10860                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10861             }
10862             
10863             if(this.labelmd > 0){
10864                 labelCfg.cls += ' col-md-' + this.labelmd;
10865                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10866             }
10867             
10868             if(this.labelsm > 0){
10869                 labelCfg.cls += ' col-sm-' + this.labelsm;
10870                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10871             }
10872             
10873             if(this.labelxs > 0){
10874                 labelCfg.cls += ' col-xs-' + this.labelxs;
10875                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10876             }
10877             
10878             
10879         } else if ( this.fieldLabel.length) {
10880                 
10881             
10882             
10883             cfg.cn = [
10884                 {
10885                     tag : 'i',
10886                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10887                     tooltip : 'This field is required',
10888                     style : this.allowBlank ? ' display:none' : '' 
10889                 },
10890                 {
10891                     tag: 'label',
10892                    //cls : 'input-group-addon',
10893                     html : this.fieldLabel
10894
10895                 },
10896
10897                inputblock
10898
10899            ];
10900            
10901            if(this.indicatorpos == 'right'){
10902        
10903                 cfg.cn = [
10904                     {
10905                         tag: 'label',
10906                        //cls : 'input-group-addon',
10907                         html : this.fieldLabel
10908
10909                     },
10910                     {
10911                         tag : 'i',
10912                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10913                         tooltip : 'This field is required',
10914                         style : this.allowBlank ? ' display:none' : '' 
10915                     },
10916
10917                    inputblock
10918
10919                ];
10920
10921             }
10922
10923         } else {
10924             
10925             cfg.cn = [
10926
10927                     inputblock
10928
10929             ];
10930                 
10931                 
10932         };
10933         
10934         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10935            cfg.cls += ' navbar-form';
10936         }
10937         
10938         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10939             // on BS4 we do this only if not form 
10940             cfg.cls += ' navbar-form';
10941             cfg.tag = 'li';
10942         }
10943         
10944         return cfg;
10945         
10946     },
10947     /**
10948      * return the real input element.
10949      */
10950     inputEl: function ()
10951     {
10952         return this.el.select('input.form-control',true).first();
10953     },
10954     
10955     tooltipEl : function()
10956     {
10957         return this.inputEl();
10958     },
10959     
10960     indicatorEl : function()
10961     {
10962         if (Roo.bootstrap.version == 4) {
10963             return false; // not enabled in v4 yet.
10964         }
10965         
10966         var indicator = this.el.select('i.roo-required-indicator',true).first();
10967         
10968         if(!indicator){
10969             return false;
10970         }
10971         
10972         return indicator;
10973         
10974     },
10975     
10976     setDisabled : function(v)
10977     {
10978         var i  = this.inputEl().dom;
10979         if (!v) {
10980             i.removeAttribute('disabled');
10981             return;
10982             
10983         }
10984         i.setAttribute('disabled','true');
10985     },
10986     initEvents : function()
10987     {
10988           
10989         this.inputEl().on("keydown" , this.fireKey,  this);
10990         this.inputEl().on("focus", this.onFocus,  this);
10991         this.inputEl().on("blur", this.onBlur,  this);
10992         
10993         this.inputEl().relayEvent('keyup', this);
10994         
10995         this.indicator = this.indicatorEl();
10996         
10997         if(this.indicator){
10998             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10999         }
11000  
11001         // reference to original value for reset
11002         this.originalValue = this.getValue();
11003         //Roo.form.TextField.superclass.initEvents.call(this);
11004         if(this.validationEvent == 'keyup'){
11005             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11006             this.inputEl().on('keyup', this.filterValidation, this);
11007         }
11008         else if(this.validationEvent !== false){
11009             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11010         }
11011         
11012         if(this.selectOnFocus){
11013             this.on("focus", this.preFocus, this);
11014             
11015         }
11016         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11017             this.inputEl().on("keypress", this.filterKeys, this);
11018         } else {
11019             this.inputEl().relayEvent('keypress', this);
11020         }
11021        /* if(this.grow){
11022             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11023             this.el.on("click", this.autoSize,  this);
11024         }
11025         */
11026         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11027             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11028         }
11029         
11030         if (typeof(this.before) == 'object') {
11031             this.before.render(this.el.select('.roo-input-before',true).first());
11032         }
11033         if (typeof(this.after) == 'object') {
11034             this.after.render(this.el.select('.roo-input-after',true).first());
11035         }
11036         
11037         this.inputEl().on('change', this.onChange, this);
11038         
11039     },
11040     filterValidation : function(e){
11041         if(!e.isNavKeyPress()){
11042             this.validationTask.delay(this.validationDelay);
11043         }
11044     },
11045      /**
11046      * Validates the field value
11047      * @return {Boolean} True if the value is valid, else false
11048      */
11049     validate : function(){
11050         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11051         if(this.disabled || this.validateValue(this.getRawValue())){
11052             this.markValid();
11053             return true;
11054         }
11055         
11056         this.markInvalid();
11057         return false;
11058     },
11059     
11060     
11061     /**
11062      * Validates a value according to the field's validation rules and marks the field as invalid
11063      * if the validation fails
11064      * @param {Mixed} value The value to validate
11065      * @return {Boolean} True if the value is valid, else false
11066      */
11067     validateValue : function(value)
11068     {
11069         if(this.getVisibilityEl().hasClass('hidden')){
11070             return true;
11071         }
11072         
11073         if(value.length < 1)  { // if it's blank
11074             if(this.allowBlank){
11075                 return true;
11076             }
11077             return false;
11078         }
11079         
11080         if(value.length < this.minLength){
11081             return false;
11082         }
11083         if(value.length > this.maxLength){
11084             return false;
11085         }
11086         if(this.vtype){
11087             var vt = Roo.form.VTypes;
11088             if(!vt[this.vtype](value, this)){
11089                 return false;
11090             }
11091         }
11092         if(typeof this.validator == "function"){
11093             var msg = this.validator(value);
11094             if(msg !== true){
11095                 return false;
11096             }
11097             if (typeof(msg) == 'string') {
11098                 this.invalidText = msg;
11099             }
11100         }
11101         
11102         if(this.regex && !this.regex.test(value)){
11103             return false;
11104         }
11105         
11106         return true;
11107     },
11108     
11109      // private
11110     fireKey : function(e){
11111         //Roo.log('field ' + e.getKey());
11112         if(e.isNavKeyPress()){
11113             this.fireEvent("specialkey", this, e);
11114         }
11115     },
11116     focus : function (selectText){
11117         if(this.rendered){
11118             this.inputEl().focus();
11119             if(selectText === true){
11120                 this.inputEl().dom.select();
11121             }
11122         }
11123         return this;
11124     } ,
11125     
11126     onFocus : function(){
11127         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11128            // this.el.addClass(this.focusClass);
11129         }
11130         if(!this.hasFocus){
11131             this.hasFocus = true;
11132             this.startValue = this.getValue();
11133             this.fireEvent("focus", this);
11134         }
11135     },
11136     
11137     beforeBlur : Roo.emptyFn,
11138
11139     
11140     // private
11141     onBlur : function(){
11142         this.beforeBlur();
11143         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11144             //this.el.removeClass(this.focusClass);
11145         }
11146         this.hasFocus = false;
11147         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11148             this.validate();
11149         }
11150         var v = this.getValue();
11151         if(String(v) !== String(this.startValue)){
11152             this.fireEvent('change', this, v, this.startValue);
11153         }
11154         this.fireEvent("blur", this);
11155     },
11156     
11157     onChange : function(e)
11158     {
11159         var v = this.getValue();
11160         if(String(v) !== String(this.startValue)){
11161             this.fireEvent('change', this, v, this.startValue);
11162         }
11163         
11164     },
11165     
11166     /**
11167      * Resets the current field value to the originally loaded value and clears any validation messages
11168      */
11169     reset : function(){
11170         this.setValue(this.originalValue);
11171         this.validate();
11172     },
11173      /**
11174      * Returns the name of the field
11175      * @return {Mixed} name The name field
11176      */
11177     getName: function(){
11178         return this.name;
11179     },
11180      /**
11181      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11182      * @return {Mixed} value The field value
11183      */
11184     getValue : function(){
11185         
11186         var v = this.inputEl().getValue();
11187         
11188         return v;
11189     },
11190     /**
11191      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11192      * @return {Mixed} value The field value
11193      */
11194     getRawValue : function(){
11195         var v = this.inputEl().getValue();
11196         
11197         return v;
11198     },
11199     
11200     /**
11201      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11202      * @param {Mixed} value The value to set
11203      */
11204     setRawValue : function(v){
11205         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11206     },
11207     
11208     selectText : function(start, end){
11209         var v = this.getRawValue();
11210         if(v.length > 0){
11211             start = start === undefined ? 0 : start;
11212             end = end === undefined ? v.length : end;
11213             var d = this.inputEl().dom;
11214             if(d.setSelectionRange){
11215                 d.setSelectionRange(start, end);
11216             }else if(d.createTextRange){
11217                 var range = d.createTextRange();
11218                 range.moveStart("character", start);
11219                 range.moveEnd("character", v.length-end);
11220                 range.select();
11221             }
11222         }
11223     },
11224     
11225     /**
11226      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11227      * @param {Mixed} value The value to set
11228      */
11229     setValue : function(v){
11230         this.value = v;
11231         if(this.rendered){
11232             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11233             this.validate();
11234         }
11235     },
11236     
11237     /*
11238     processValue : function(value){
11239         if(this.stripCharsRe){
11240             var newValue = value.replace(this.stripCharsRe, '');
11241             if(newValue !== value){
11242                 this.setRawValue(newValue);
11243                 return newValue;
11244             }
11245         }
11246         return value;
11247     },
11248   */
11249     preFocus : function(){
11250         
11251         if(this.selectOnFocus){
11252             this.inputEl().dom.select();
11253         }
11254     },
11255     filterKeys : function(e){
11256         var k = e.getKey();
11257         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11258             return;
11259         }
11260         var c = e.getCharCode(), cc = String.fromCharCode(c);
11261         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11262             return;
11263         }
11264         if(!this.maskRe.test(cc)){
11265             e.stopEvent();
11266         }
11267     },
11268      /**
11269      * Clear any invalid styles/messages for this field
11270      */
11271     clearInvalid : function(){
11272         
11273         if(!this.el || this.preventMark){ // not rendered
11274             return;
11275         }
11276         
11277         
11278         this.el.removeClass([this.invalidClass, 'is-invalid']);
11279         
11280         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11281             
11282             var feedback = this.el.select('.form-control-feedback', true).first();
11283             
11284             if(feedback){
11285                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11286             }
11287             
11288         }
11289         
11290         if(this.indicator){
11291             this.indicator.removeClass('visible');
11292             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11293         }
11294         
11295         this.fireEvent('valid', this);
11296     },
11297     
11298      /**
11299      * Mark this field as valid
11300      */
11301     markValid : function()
11302     {
11303         if(!this.el  || this.preventMark){ // not rendered...
11304             return;
11305         }
11306         
11307         this.el.removeClass([this.invalidClass, this.validClass]);
11308         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11309
11310         var feedback = this.el.select('.form-control-feedback', true).first();
11311             
11312         if(feedback){
11313             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11314         }
11315         
11316         if(this.indicator){
11317             this.indicator.removeClass('visible');
11318             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11319         }
11320         
11321         if(this.disabled){
11322             return;
11323         }
11324         
11325            
11326         if(this.allowBlank && !this.getRawValue().length){
11327             return;
11328         }
11329         if (Roo.bootstrap.version == 3) {
11330             this.el.addClass(this.validClass);
11331         } else {
11332             this.inputEl().addClass('is-valid');
11333         }
11334
11335         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11336             
11337             var feedback = this.el.select('.form-control-feedback', true).first();
11338             
11339             if(feedback){
11340                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11341                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11342             }
11343             
11344         }
11345         
11346         this.fireEvent('valid', this);
11347     },
11348     
11349      /**
11350      * Mark this field as invalid
11351      * @param {String} msg The validation message
11352      */
11353     markInvalid : function(msg)
11354     {
11355         if(!this.el  || this.preventMark){ // not rendered
11356             return;
11357         }
11358         
11359         this.el.removeClass([this.invalidClass, this.validClass]);
11360         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11361         
11362         var feedback = this.el.select('.form-control-feedback', true).first();
11363             
11364         if(feedback){
11365             this.el.select('.form-control-feedback', true).first().removeClass(
11366                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11367         }
11368
11369         if(this.disabled){
11370             return;
11371         }
11372         
11373         if(this.allowBlank && !this.getRawValue().length){
11374             return;
11375         }
11376         
11377         if(this.indicator){
11378             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11379             this.indicator.addClass('visible');
11380         }
11381         if (Roo.bootstrap.version == 3) {
11382             this.el.addClass(this.invalidClass);
11383         } else {
11384             this.inputEl().addClass('is-invalid');
11385         }
11386         
11387         
11388         
11389         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11390             
11391             var feedback = this.el.select('.form-control-feedback', true).first();
11392             
11393             if(feedback){
11394                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11395                 
11396                 if(this.getValue().length || this.forceFeedback){
11397                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11398                 }
11399                 
11400             }
11401             
11402         }
11403         
11404         this.fireEvent('invalid', this, msg);
11405     },
11406     // private
11407     SafariOnKeyDown : function(event)
11408     {
11409         // this is a workaround for a password hang bug on chrome/ webkit.
11410         if (this.inputEl().dom.type != 'password') {
11411             return;
11412         }
11413         
11414         var isSelectAll = false;
11415         
11416         if(this.inputEl().dom.selectionEnd > 0){
11417             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11418         }
11419         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11420             event.preventDefault();
11421             this.setValue('');
11422             return;
11423         }
11424         
11425         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11426             
11427             event.preventDefault();
11428             // this is very hacky as keydown always get's upper case.
11429             //
11430             var cc = String.fromCharCode(event.getCharCode());
11431             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11432             
11433         }
11434     },
11435     adjustWidth : function(tag, w){
11436         tag = tag.toLowerCase();
11437         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11438             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11439                 if(tag == 'input'){
11440                     return w + 2;
11441                 }
11442                 if(tag == 'textarea'){
11443                     return w-2;
11444                 }
11445             }else if(Roo.isOpera){
11446                 if(tag == 'input'){
11447                     return w + 2;
11448                 }
11449                 if(tag == 'textarea'){
11450                     return w-2;
11451                 }
11452             }
11453         }
11454         return w;
11455     },
11456     
11457     setFieldLabel : function(v)
11458     {
11459         if(!this.rendered){
11460             return;
11461         }
11462         
11463         if(this.indicatorEl()){
11464             var ar = this.el.select('label > span',true);
11465             
11466             if (ar.elements.length) {
11467                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11468                 this.fieldLabel = v;
11469                 return;
11470             }
11471             
11472             var br = this.el.select('label',true);
11473             
11474             if(br.elements.length) {
11475                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             Roo.log('Cannot Found any of label > span || label in input');
11481             return;
11482         }
11483         
11484         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11485         this.fieldLabel = v;
11486         
11487         
11488     }
11489 });
11490
11491  
11492 /*
11493  * - LGPL
11494  *
11495  * Input
11496  * 
11497  */
11498
11499 /**
11500  * @class Roo.bootstrap.TextArea
11501  * @extends Roo.bootstrap.Input
11502  * Bootstrap TextArea class
11503  * @cfg {Number} cols Specifies the visible width of a text area
11504  * @cfg {Number} rows Specifies the visible number of lines in a text area
11505  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11506  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11507  * @cfg {string} html text
11508  * 
11509  * @constructor
11510  * Create a new TextArea
11511  * @param {Object} config The config object
11512  */
11513
11514 Roo.bootstrap.TextArea = function(config){
11515     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11516    
11517 };
11518
11519 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11520      
11521     cols : false,
11522     rows : 5,
11523     readOnly : false,
11524     warp : 'soft',
11525     resize : false,
11526     value: false,
11527     html: false,
11528     
11529     getAutoCreate : function(){
11530         
11531         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11532         
11533         var id = Roo.id();
11534         
11535         var cfg = {};
11536         
11537         if(this.inputType != 'hidden'){
11538             cfg.cls = 'form-group' //input-group
11539         }
11540         
11541         var input =  {
11542             tag: 'textarea',
11543             id : id,
11544             warp : this.warp,
11545             rows : this.rows,
11546             value : this.value || '',
11547             html: this.html || '',
11548             cls : 'form-control',
11549             placeholder : this.placeholder || '' 
11550             
11551         };
11552         
11553         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11554             input.maxLength = this.maxLength;
11555         }
11556         
11557         if(this.resize){
11558             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11559         }
11560         
11561         if(this.cols){
11562             input.cols = this.cols;
11563         }
11564         
11565         if (this.readOnly) {
11566             input.readonly = true;
11567         }
11568         
11569         if (this.name) {
11570             input.name = this.name;
11571         }
11572         
11573         if (this.size) {
11574             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11575         }
11576         
11577         var settings=this;
11578         ['xs','sm','md','lg'].map(function(size){
11579             if (settings[size]) {
11580                 cfg.cls += ' col-' + size + '-' + settings[size];
11581             }
11582         });
11583         
11584         var inputblock = input;
11585         
11586         if(this.hasFeedback && !this.allowBlank){
11587             
11588             var feedback = {
11589                 tag: 'span',
11590                 cls: 'glyphicon form-control-feedback'
11591             };
11592
11593             inputblock = {
11594                 cls : 'has-feedback',
11595                 cn :  [
11596                     input,
11597                     feedback
11598                 ] 
11599             };  
11600         }
11601         
11602         
11603         if (this.before || this.after) {
11604             
11605             inputblock = {
11606                 cls : 'input-group',
11607                 cn :  [] 
11608             };
11609             if (this.before) {
11610                 inputblock.cn.push({
11611                     tag :'span',
11612                     cls : 'input-group-addon',
11613                     html : this.before
11614                 });
11615             }
11616             
11617             inputblock.cn.push(input);
11618             
11619             if(this.hasFeedback && !this.allowBlank){
11620                 inputblock.cls += ' has-feedback';
11621                 inputblock.cn.push(feedback);
11622             }
11623             
11624             if (this.after) {
11625                 inputblock.cn.push({
11626                     tag :'span',
11627                     cls : 'input-group-addon',
11628                     html : this.after
11629                 });
11630             }
11631             
11632         }
11633         
11634         if (align ==='left' && this.fieldLabel.length) {
11635             cfg.cn = [
11636                 {
11637                     tag: 'label',
11638                     'for' :  id,
11639                     cls : 'control-label',
11640                     html : this.fieldLabel
11641                 },
11642                 {
11643                     cls : "",
11644                     cn: [
11645                         inputblock
11646                     ]
11647                 }
11648
11649             ];
11650             
11651             if(this.labelWidth > 12){
11652                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11653             }
11654
11655             if(this.labelWidth < 13 && this.labelmd == 0){
11656                 this.labelmd = this.labelWidth;
11657             }
11658
11659             if(this.labellg > 0){
11660                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11661                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11662             }
11663
11664             if(this.labelmd > 0){
11665                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11666                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11667             }
11668
11669             if(this.labelsm > 0){
11670                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11671                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11672             }
11673
11674             if(this.labelxs > 0){
11675                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11676                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11677             }
11678             
11679         } else if ( this.fieldLabel.length) {
11680             cfg.cn = [
11681
11682                {
11683                    tag: 'label',
11684                    //cls : 'input-group-addon',
11685                    html : this.fieldLabel
11686
11687                },
11688
11689                inputblock
11690
11691            ];
11692
11693         } else {
11694
11695             cfg.cn = [
11696
11697                 inputblock
11698
11699             ];
11700                 
11701         }
11702         
11703         if (this.disabled) {
11704             input.disabled=true;
11705         }
11706         
11707         return cfg;
11708         
11709     },
11710     /**
11711      * return the real textarea element.
11712      */
11713     inputEl: function ()
11714     {
11715         return this.el.select('textarea.form-control',true).first();
11716     },
11717     
11718     /**
11719      * Clear any invalid styles/messages for this field
11720      */
11721     clearInvalid : function()
11722     {
11723         
11724         if(!this.el || this.preventMark){ // not rendered
11725             return;
11726         }
11727         
11728         var label = this.el.select('label', true).first();
11729         var icon = this.el.select('i.fa-star', true).first();
11730         
11731         if(label && icon){
11732             icon.remove();
11733         }
11734         this.el.removeClass( this.validClass);
11735         this.inputEl().removeClass('is-invalid');
11736          
11737         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11738             
11739             var feedback = this.el.select('.form-control-feedback', true).first();
11740             
11741             if(feedback){
11742                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11743             }
11744             
11745         }
11746         
11747         this.fireEvent('valid', this);
11748     },
11749     
11750      /**
11751      * Mark this field as valid
11752      */
11753     markValid : function()
11754     {
11755         if(!this.el  || this.preventMark){ // not rendered
11756             return;
11757         }
11758         
11759         this.el.removeClass([this.invalidClass, this.validClass]);
11760         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11761         
11762         var feedback = this.el.select('.form-control-feedback', true).first();
11763             
11764         if(feedback){
11765             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11766         }
11767
11768         if(this.disabled || this.allowBlank){
11769             return;
11770         }
11771         
11772         var label = this.el.select('label', true).first();
11773         var icon = this.el.select('i.fa-star', true).first();
11774         
11775         if(label && icon){
11776             icon.remove();
11777         }
11778         if (Roo.bootstrap.version == 3) {
11779             this.el.addClass(this.validClass);
11780         } else {
11781             this.inputEl().addClass('is-valid');
11782         }
11783         
11784         
11785         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11786             
11787             var feedback = this.el.select('.form-control-feedback', true).first();
11788             
11789             if(feedback){
11790                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11791                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11792             }
11793             
11794         }
11795         
11796         this.fireEvent('valid', this);
11797     },
11798     
11799      /**
11800      * Mark this field as invalid
11801      * @param {String} msg The validation message
11802      */
11803     markInvalid : function(msg)
11804     {
11805         if(!this.el  || this.preventMark){ // not rendered
11806             return;
11807         }
11808         
11809         this.el.removeClass([this.invalidClass, this.validClass]);
11810         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11811         
11812         var feedback = this.el.select('.form-control-feedback', true).first();
11813             
11814         if(feedback){
11815             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11816         }
11817
11818         if(this.disabled || this.allowBlank){
11819             return;
11820         }
11821         
11822         var label = this.el.select('label', true).first();
11823         var icon = this.el.select('i.fa-star', true).first();
11824         
11825         if(!this.getValue().length && label && !icon){
11826             this.el.createChild({
11827                 tag : 'i',
11828                 cls : 'text-danger fa fa-lg fa-star',
11829                 tooltip : 'This field is required',
11830                 style : 'margin-right:5px;'
11831             }, label, true);
11832         }
11833         
11834         if (Roo.bootstrap.version == 3) {
11835             this.el.addClass(this.invalidClass);
11836         } else {
11837             this.inputEl().addClass('is-invalid');
11838         }
11839         
11840         // fixme ... this may be depricated need to test..
11841         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11842             
11843             var feedback = this.el.select('.form-control-feedback', true).first();
11844             
11845             if(feedback){
11846                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11847                 
11848                 if(this.getValue().length || this.forceFeedback){
11849                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11850                 }
11851                 
11852             }
11853             
11854         }
11855         
11856         this.fireEvent('invalid', this, msg);
11857     }
11858 });
11859
11860  
11861 /*
11862  * - LGPL
11863  *
11864  * trigger field - base class for combo..
11865  * 
11866  */
11867  
11868 /**
11869  * @class Roo.bootstrap.TriggerField
11870  * @extends Roo.bootstrap.Input
11871  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11872  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11873  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11874  * for which you can provide a custom implementation.  For example:
11875  * <pre><code>
11876 var trigger = new Roo.bootstrap.TriggerField();
11877 trigger.onTriggerClick = myTriggerFn;
11878 trigger.applyTo('my-field');
11879 </code></pre>
11880  *
11881  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11882  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11883  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11884  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11885  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11886
11887  * @constructor
11888  * Create a new TriggerField.
11889  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11890  * to the base TextField)
11891  */
11892 Roo.bootstrap.TriggerField = function(config){
11893     this.mimicing = false;
11894     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11895 };
11896
11897 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11898     /**
11899      * @cfg {String} triggerClass A CSS class to apply to the trigger
11900      */
11901      /**
11902      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11903      */
11904     hideTrigger:false,
11905
11906     /**
11907      * @cfg {Boolean} removable (true|false) special filter default false
11908      */
11909     removable : false,
11910     
11911     /** @cfg {Boolean} grow @hide */
11912     /** @cfg {Number} growMin @hide */
11913     /** @cfg {Number} growMax @hide */
11914
11915     /**
11916      * @hide 
11917      * @method
11918      */
11919     autoSize: Roo.emptyFn,
11920     // private
11921     monitorTab : true,
11922     // private
11923     deferHeight : true,
11924
11925     
11926     actionMode : 'wrap',
11927     
11928     caret : false,
11929     
11930     
11931     getAutoCreate : function(){
11932        
11933         var align = this.labelAlign || this.parentLabelAlign();
11934         
11935         var id = Roo.id();
11936         
11937         var cfg = {
11938             cls: 'form-group' //input-group
11939         };
11940         
11941         
11942         var input =  {
11943             tag: 'input',
11944             id : id,
11945             type : this.inputType,
11946             cls : 'form-control',
11947             autocomplete: 'new-password',
11948             placeholder : this.placeholder || '' 
11949             
11950         };
11951         if (this.name) {
11952             input.name = this.name;
11953         }
11954         if (this.size) {
11955             input.cls += ' input-' + this.size;
11956         }
11957         
11958         if (this.disabled) {
11959             input.disabled=true;
11960         }
11961         
11962         var inputblock = input;
11963         
11964         if(this.hasFeedback && !this.allowBlank){
11965             
11966             var feedback = {
11967                 tag: 'span',
11968                 cls: 'glyphicon form-control-feedback'
11969             };
11970             
11971             if(this.removable && !this.editable  ){
11972                 inputblock = {
11973                     cls : 'has-feedback',
11974                     cn :  [
11975                         inputblock,
11976                         {
11977                             tag: 'button',
11978                             html : 'x',
11979                             cls : 'roo-combo-removable-btn close'
11980                         },
11981                         feedback
11982                     ] 
11983                 };
11984             } else {
11985                 inputblock = {
11986                     cls : 'has-feedback',
11987                     cn :  [
11988                         inputblock,
11989                         feedback
11990                     ] 
11991                 };
11992             }
11993
11994         } else {
11995             if(this.removable && !this.editable ){
11996                 inputblock = {
11997                     cls : 'roo-removable',
11998                     cn :  [
11999                         inputblock,
12000                         {
12001                             tag: 'button',
12002                             html : 'x',
12003                             cls : 'roo-combo-removable-btn close'
12004                         }
12005                     ] 
12006                 };
12007             }
12008         }
12009         
12010         if (this.before || this.after) {
12011             
12012             inputblock = {
12013                 cls : 'input-group',
12014                 cn :  [] 
12015             };
12016             if (this.before) {
12017                 inputblock.cn.push({
12018                     tag :'span',
12019                     cls : 'input-group-addon input-group-prepend input-group-text',
12020                     html : this.before
12021                 });
12022             }
12023             
12024             inputblock.cn.push(input);
12025             
12026             if(this.hasFeedback && !this.allowBlank){
12027                 inputblock.cls += ' has-feedback';
12028                 inputblock.cn.push(feedback);
12029             }
12030             
12031             if (this.after) {
12032                 inputblock.cn.push({
12033                     tag :'span',
12034                     cls : 'input-group-addon input-group-append input-group-text',
12035                     html : this.after
12036                 });
12037             }
12038             
12039         };
12040         
12041       
12042         
12043         var ibwrap = inputblock;
12044         
12045         if(this.multiple){
12046             ibwrap = {
12047                 tag: 'ul',
12048                 cls: 'roo-select2-choices',
12049                 cn:[
12050                     {
12051                         tag: 'li',
12052                         cls: 'roo-select2-search-field',
12053                         cn: [
12054
12055                             inputblock
12056                         ]
12057                     }
12058                 ]
12059             };
12060                 
12061         }
12062         
12063         var combobox = {
12064             cls: 'roo-select2-container input-group',
12065             cn: [
12066                  {
12067                     tag: 'input',
12068                     type : 'hidden',
12069                     cls: 'form-hidden-field'
12070                 },
12071                 ibwrap
12072             ]
12073         };
12074         
12075         if(!this.multiple && this.showToggleBtn){
12076             
12077             var caret = {
12078                         tag: 'span',
12079                         cls: 'caret'
12080              };
12081             if (this.caret != false) {
12082                 caret = {
12083                      tag: 'i',
12084                      cls: 'fa fa-' + this.caret
12085                 };
12086                 
12087             }
12088             
12089             combobox.cn.push({
12090                 tag :'span',
12091                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12092                 cn : [
12093                     Roo.bootstrap.version == 3 ? caret : '',
12094                     {
12095                         tag: 'span',
12096                         cls: 'combobox-clear',
12097                         cn  : [
12098                             {
12099                                 tag : 'i',
12100                                 cls: 'icon-remove'
12101                             }
12102                         ]
12103                     }
12104                 ]
12105
12106             })
12107         }
12108         
12109         if(this.multiple){
12110             combobox.cls += ' roo-select2-container-multi';
12111         }
12112          var indicator = {
12113             tag : 'i',
12114             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12115             tooltip : 'This field is required'
12116         };
12117         if (Roo.bootstrap.version == 4) {
12118             indicator = {
12119                 tag : 'i',
12120                 style : 'display:none'
12121             };
12122         }
12123         
12124         
12125         if (align ==='left' && this.fieldLabel.length) {
12126             
12127             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12128
12129             cfg.cn = [
12130                 indicator,
12131                 {
12132                     tag: 'label',
12133                     'for' :  id,
12134                     cls : 'control-label',
12135                     html : this.fieldLabel
12136
12137                 },
12138                 {
12139                     cls : "", 
12140                     cn: [
12141                         combobox
12142                     ]
12143                 }
12144
12145             ];
12146             
12147             var labelCfg = cfg.cn[1];
12148             var contentCfg = cfg.cn[2];
12149             
12150             if(this.indicatorpos == 'right'){
12151                 cfg.cn = [
12152                     {
12153                         tag: 'label',
12154                         'for' :  id,
12155                         cls : 'control-label',
12156                         cn : [
12157                             {
12158                                 tag : 'span',
12159                                 html : this.fieldLabel
12160                             },
12161                             indicator
12162                         ]
12163                     },
12164                     {
12165                         cls : "", 
12166                         cn: [
12167                             combobox
12168                         ]
12169                     }
12170
12171                 ];
12172                 
12173                 labelCfg = cfg.cn[0];
12174                 contentCfg = cfg.cn[1];
12175             }
12176             
12177             if(this.labelWidth > 12){
12178                 labelCfg.style = "width: " + this.labelWidth + 'px';
12179             }
12180             
12181             if(this.labelWidth < 13 && this.labelmd == 0){
12182                 this.labelmd = this.labelWidth;
12183             }
12184             
12185             if(this.labellg > 0){
12186                 labelCfg.cls += ' col-lg-' + this.labellg;
12187                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12188             }
12189             
12190             if(this.labelmd > 0){
12191                 labelCfg.cls += ' col-md-' + this.labelmd;
12192                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12193             }
12194             
12195             if(this.labelsm > 0){
12196                 labelCfg.cls += ' col-sm-' + this.labelsm;
12197                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12198             }
12199             
12200             if(this.labelxs > 0){
12201                 labelCfg.cls += ' col-xs-' + this.labelxs;
12202                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12203             }
12204             
12205         } else if ( this.fieldLabel.length) {
12206 //                Roo.log(" label");
12207             cfg.cn = [
12208                 indicator,
12209                {
12210                    tag: 'label',
12211                    //cls : 'input-group-addon',
12212                    html : this.fieldLabel
12213
12214                },
12215
12216                combobox
12217
12218             ];
12219             
12220             if(this.indicatorpos == 'right'){
12221                 
12222                 cfg.cn = [
12223                     {
12224                        tag: 'label',
12225                        cn : [
12226                            {
12227                                tag : 'span',
12228                                html : this.fieldLabel
12229                            },
12230                            indicator
12231                        ]
12232
12233                     },
12234                     combobox
12235
12236                 ];
12237
12238             }
12239
12240         } else {
12241             
12242 //                Roo.log(" no label && no align");
12243                 cfg = combobox
12244                      
12245                 
12246         }
12247         
12248         var settings=this;
12249         ['xs','sm','md','lg'].map(function(size){
12250             if (settings[size]) {
12251                 cfg.cls += ' col-' + size + '-' + settings[size];
12252             }
12253         });
12254         
12255         return cfg;
12256         
12257     },
12258     
12259     
12260     
12261     // private
12262     onResize : function(w, h){
12263 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12264 //        if(typeof w == 'number'){
12265 //            var x = w - this.trigger.getWidth();
12266 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12267 //            this.trigger.setStyle('left', x+'px');
12268 //        }
12269     },
12270
12271     // private
12272     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12273
12274     // private
12275     getResizeEl : function(){
12276         return this.inputEl();
12277     },
12278
12279     // private
12280     getPositionEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     alignErrorIcon : function(){
12286         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12287     },
12288
12289     // private
12290     initEvents : function(){
12291         
12292         this.createList();
12293         
12294         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12295         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12296         if(!this.multiple && this.showToggleBtn){
12297             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12298             if(this.hideTrigger){
12299                 this.trigger.setDisplayed(false);
12300             }
12301             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12302         }
12303         
12304         if(this.multiple){
12305             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12306         }
12307         
12308         if(this.removable && !this.editable && !this.tickable){
12309             var close = this.closeTriggerEl();
12310             
12311             if(close){
12312                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12313                 close.on('click', this.removeBtnClick, this, close);
12314             }
12315         }
12316         
12317         //this.trigger.addClassOnOver('x-form-trigger-over');
12318         //this.trigger.addClassOnClick('x-form-trigger-click');
12319         
12320         //if(!this.width){
12321         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12322         //}
12323     },
12324     
12325     closeTriggerEl : function()
12326     {
12327         var close = this.el.select('.roo-combo-removable-btn', true).first();
12328         return close ? close : false;
12329     },
12330     
12331     removeBtnClick : function(e, h, el)
12332     {
12333         e.preventDefault();
12334         
12335         if(this.fireEvent("remove", this) !== false){
12336             this.reset();
12337             this.fireEvent("afterremove", this)
12338         }
12339     },
12340     
12341     createList : function()
12342     {
12343         this.list = Roo.get(document.body).createChild({
12344             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12345             cls: 'typeahead typeahead-long dropdown-menu shadow',
12346             style: 'display:none'
12347         });
12348         
12349         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12350         
12351     },
12352
12353     // private
12354     initTrigger : function(){
12355        
12356     },
12357
12358     // private
12359     onDestroy : function(){
12360         if(this.trigger){
12361             this.trigger.removeAllListeners();
12362           //  this.trigger.remove();
12363         }
12364         //if(this.wrap){
12365         //    this.wrap.remove();
12366         //}
12367         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12368     },
12369
12370     // private
12371     onFocus : function(){
12372         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12373         /*
12374         if(!this.mimicing){
12375             this.wrap.addClass('x-trigger-wrap-focus');
12376             this.mimicing = true;
12377             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12378             if(this.monitorTab){
12379                 this.el.on("keydown", this.checkTab, this);
12380             }
12381         }
12382         */
12383     },
12384
12385     // private
12386     checkTab : function(e){
12387         if(e.getKey() == e.TAB){
12388             this.triggerBlur();
12389         }
12390     },
12391
12392     // private
12393     onBlur : function(){
12394         // do nothing
12395     },
12396
12397     // private
12398     mimicBlur : function(e, t){
12399         /*
12400         if(!this.wrap.contains(t) && this.validateBlur()){
12401             this.triggerBlur();
12402         }
12403         */
12404     },
12405
12406     // private
12407     triggerBlur : function(){
12408         this.mimicing = false;
12409         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12410         if(this.monitorTab){
12411             this.el.un("keydown", this.checkTab, this);
12412         }
12413         //this.wrap.removeClass('x-trigger-wrap-focus');
12414         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12415     },
12416
12417     // private
12418     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12419     validateBlur : function(e, t){
12420         return true;
12421     },
12422
12423     // private
12424     onDisable : function(){
12425         this.inputEl().dom.disabled = true;
12426         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12427         //if(this.wrap){
12428         //    this.wrap.addClass('x-item-disabled');
12429         //}
12430     },
12431
12432     // private
12433     onEnable : function(){
12434         this.inputEl().dom.disabled = false;
12435         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12436         //if(this.wrap){
12437         //    this.el.removeClass('x-item-disabled');
12438         //}
12439     },
12440
12441     // private
12442     onShow : function(){
12443         var ae = this.getActionEl();
12444         
12445         if(ae){
12446             ae.dom.style.display = '';
12447             ae.dom.style.visibility = 'visible';
12448         }
12449     },
12450
12451     // private
12452     
12453     onHide : function(){
12454         var ae = this.getActionEl();
12455         ae.dom.style.display = 'none';
12456     },
12457
12458     /**
12459      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12460      * by an implementing function.
12461      * @method
12462      * @param {EventObject} e
12463      */
12464     onTriggerClick : Roo.emptyFn
12465 });
12466  
12467 /*
12468 * Licence: LGPL
12469 */
12470
12471 /**
12472  * @class Roo.bootstrap.CardUploader
12473  * @extends Roo.bootstrap.Button
12474  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12475  * @cfg {Number} errorTimeout default 3000
12476  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12477  * @cfg {Array}  html The button text.
12478
12479  *
12480  * @constructor
12481  * Create a new CardUploader
12482  * @param {Object} config The config object
12483  */
12484
12485 Roo.bootstrap.CardUploader = function(config){
12486     
12487  
12488     
12489     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12490     
12491     
12492     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12493         return r.data.id
12494         });
12495     
12496     
12497 };
12498
12499 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12500     
12501      
12502     errorTimeout : 3000,
12503      
12504     images : false,
12505    
12506     fileCollection : false,
12507     allowBlank : true,
12508     
12509     getAutoCreate : function()
12510     {
12511         
12512         var cfg =  {
12513             cls :'form-group' ,
12514             cn : [
12515                
12516                 {
12517                     tag: 'label',
12518                    //cls : 'input-group-addon',
12519                     html : this.fieldLabel
12520
12521                 },
12522
12523                 {
12524                     tag: 'input',
12525                     type : 'hidden',
12526                     value : this.value,
12527                     cls : 'd-none  form-control'
12528                 },
12529                 
12530                 {
12531                     tag: 'input',
12532                     multiple : 'multiple',
12533                     type : 'file',
12534                     cls : 'd-none  roo-card-upload-selector'
12535                 },
12536                 
12537                 {
12538                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12539                 },
12540                 {
12541                     cls : 'card-columns roo-card-uploader-container'
12542                 }
12543
12544             ]
12545         };
12546            
12547          
12548         return cfg;
12549     },
12550     
12551     getChildContainer : function() /// what children are added to.
12552     {
12553         return this.containerEl;
12554     },
12555    
12556     getButtonContainer : function() /// what children are added to.
12557     {
12558         return this.el.select(".roo-card-uploader-button-container").first();
12559     },
12560    
12561     initEvents : function()
12562     {
12563         
12564         Roo.bootstrap.Input.prototype.initEvents.call(this);
12565         
12566         var t = this;
12567         this.addxtype({
12568             xns: Roo.bootstrap,
12569
12570             xtype : 'Button',
12571             container_method : 'getButtonContainer' ,            
12572             html :  this.html, // fix changable?
12573             cls : 'w-100 ',
12574             listeners : {
12575                 'click' : function(btn, e) {
12576                     t.onClick(e);
12577                 }
12578             }
12579         });
12580         
12581         
12582         
12583         
12584         this.urlAPI = (window.createObjectURL && window) || 
12585                                 (window.URL && URL.revokeObjectURL && URL) || 
12586                                 (window.webkitURL && webkitURL);
12587                         
12588          
12589          
12590          
12591         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12592         
12593         this.selectorEl.on('change', this.onFileSelected, this);
12594         if (this.images) {
12595             var t = this;
12596             this.images.forEach(function(img) {
12597                 t.addCard(img)
12598             });
12599             this.images = false;
12600         }
12601         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12602          
12603        
12604     },
12605     
12606    
12607     onClick : function(e)
12608     {
12609         e.preventDefault();
12610          
12611         this.selectorEl.dom.click();
12612          
12613     },
12614     
12615     onFileSelected : function(e)
12616     {
12617         e.preventDefault();
12618         
12619         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12620             return;
12621         }
12622         
12623         Roo.each(this.selectorEl.dom.files, function(file){    
12624             this.addFile(file);
12625         }, this);
12626          
12627     },
12628     
12629       
12630     
12631       
12632     
12633     addFile : function(file)
12634     {
12635            
12636         if(typeof(file) === 'string'){
12637             throw "Add file by name?"; // should not happen
12638             return;
12639         }
12640         
12641         if(!file || !this.urlAPI){
12642             return;
12643         }
12644         
12645         // file;
12646         // file.type;
12647         
12648         var _this = this;
12649         
12650         
12651         var url = _this.urlAPI.createObjectURL( file);
12652            
12653         this.addCard({
12654             id : Roo.bootstrap.CardUploader.ID--,
12655             is_uploaded : false,
12656             src : url,
12657             title : file.name,
12658             mimetype : file.type,
12659             preview : false,
12660             is_deleted : 0
12661         })
12662         
12663     },
12664     
12665     addCard : function (data)
12666     {
12667         // hidden input element?
12668         // if the file is not an image...
12669         //then we need to use something other that and header_image
12670         var t = this;
12671         //   remove.....
12672         var footer = [
12673             {
12674                 xns : Roo.bootstrap,
12675                 xtype : 'CardFooter',
12676                 items: [
12677                     {
12678                         xns : Roo.bootstrap,
12679                         xtype : 'Element',
12680                         cls : 'd-flex',
12681                         items : [
12682                             
12683                             {
12684                                 xns : Roo.bootstrap,
12685                                 xtype : 'Button',
12686                                 html : String.format("<small>{0}</small>", data.title),
12687                                 cls : 'col-11 text-left',
12688                                 size: 'sm',
12689                                 weight: 'link',
12690                                 fa : 'download',
12691                                 listeners : {
12692                                     click : function() {
12693                                         this.downloadCard(data.id)
12694                                     }
12695                                 }
12696                             },
12697                           
12698                             {
12699                                 xns : Roo.bootstrap,
12700                                 xtype : 'Button',
12701                                 
12702                                 size : 'sm',
12703                                 weight: 'danger',
12704                                 cls : 'col-1',
12705                                 fa : 'times',
12706                                 listeners : {
12707                                     click : function() {
12708                                         t.removeCard(data.id)
12709                                     }
12710                                 }
12711                             }
12712                         ]
12713                     }
12714                     
12715                 ] 
12716             }
12717             
12718         ];
12719
12720         var cn = this.addxtype(
12721             {
12722                  
12723                 xns : Roo.bootstrap,
12724                 xtype : 'Card',
12725                 closeable : true,
12726                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12727                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12728                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12729                 data : data,
12730                 html : false,
12731                  
12732                 items : footer,
12733                 initEvents : function() {
12734                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12735                     this.imgEl = this.el.select('.card-img-top').first();
12736                     if (this.imgEl) {
12737                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12738                         this.imgEl.set({ 'pointer' : 'cursor' });
12739                                   
12740                     }
12741                     
12742                   
12743                 }
12744                 
12745             }
12746         );
12747         // dont' really need ot update items.
12748         // this.items.push(cn);
12749         this.fileCollection.add(cn);
12750         this.updateInput();
12751         
12752     },
12753     removeCard : function(id)
12754     {
12755         
12756         var card  = this.fileCollection.get(id);
12757         card.data.is_deleted = 1;
12758         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12759         this.fileCollection.remove(card);
12760         //this.items = this.items.filter(function(e) { return e != card });
12761         // dont' really need ot update items.
12762         card.el.dom.parentNode.removeChild(card.el.dom);
12763         
12764     },
12765     reset: function()
12766     {
12767         this.fileCollection.each(function(card) {
12768             card.el.dom.parentNode.removeChild(card.el.dom);    
12769         });
12770         this.fileCollection.clear();
12771         this.updateInput();
12772     },
12773     
12774     updateInput : function()
12775     {
12776         var data = [];
12777         this.fileCollection.each(function(e) {
12778             data.push(e.data);
12779         });
12780         
12781         this.inputEl().dom.value = JSON.stringify(data);
12782     }
12783     
12784     
12785 });
12786
12787
12788 Roo.bootstrap.CardUploader.ID = -1;/*
12789  * Based on:
12790  * Ext JS Library 1.1.1
12791  * Copyright(c) 2006-2007, Ext JS, LLC.
12792  *
12793  * Originally Released Under LGPL - original licence link has changed is not relivant.
12794  *
12795  * Fork - LGPL
12796  * <script type="text/javascript">
12797  */
12798
12799
12800 /**
12801  * @class Roo.data.SortTypes
12802  * @singleton
12803  * Defines the default sorting (casting?) comparison functions used when sorting data.
12804  */
12805 Roo.data.SortTypes = {
12806     /**
12807      * Default sort that does nothing
12808      * @param {Mixed} s The value being converted
12809      * @return {Mixed} The comparison value
12810      */
12811     none : function(s){
12812         return s;
12813     },
12814     
12815     /**
12816      * The regular expression used to strip tags
12817      * @type {RegExp}
12818      * @property
12819      */
12820     stripTagsRE : /<\/?[^>]+>/gi,
12821     
12822     /**
12823      * Strips all HTML tags to sort on text only
12824      * @param {Mixed} s The value being converted
12825      * @return {String} The comparison value
12826      */
12827     asText : function(s){
12828         return String(s).replace(this.stripTagsRE, "");
12829     },
12830     
12831     /**
12832      * Strips all HTML tags to sort on text only - Case insensitive
12833      * @param {Mixed} s The value being converted
12834      * @return {String} The comparison value
12835      */
12836     asUCText : function(s){
12837         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12838     },
12839     
12840     /**
12841      * Case insensitive string
12842      * @param {Mixed} s The value being converted
12843      * @return {String} The comparison value
12844      */
12845     asUCString : function(s) {
12846         return String(s).toUpperCase();
12847     },
12848     
12849     /**
12850      * Date sorting
12851      * @param {Mixed} s The value being converted
12852      * @return {Number} The comparison value
12853      */
12854     asDate : function(s) {
12855         if(!s){
12856             return 0;
12857         }
12858         if(s instanceof Date){
12859             return s.getTime();
12860         }
12861         return Date.parse(String(s));
12862     },
12863     
12864     /**
12865      * Float sorting
12866      * @param {Mixed} s The value being converted
12867      * @return {Float} The comparison value
12868      */
12869     asFloat : function(s) {
12870         var val = parseFloat(String(s).replace(/,/g, ""));
12871         if(isNaN(val)) {
12872             val = 0;
12873         }
12874         return val;
12875     },
12876     
12877     /**
12878      * Integer sorting
12879      * @param {Mixed} s The value being converted
12880      * @return {Number} The comparison value
12881      */
12882     asInt : function(s) {
12883         var val = parseInt(String(s).replace(/,/g, ""));
12884         if(isNaN(val)) {
12885             val = 0;
12886         }
12887         return val;
12888     }
12889 };/*
12890  * Based on:
12891  * Ext JS Library 1.1.1
12892  * Copyright(c) 2006-2007, Ext JS, LLC.
12893  *
12894  * Originally Released Under LGPL - original licence link has changed is not relivant.
12895  *
12896  * Fork - LGPL
12897  * <script type="text/javascript">
12898  */
12899
12900 /**
12901 * @class Roo.data.Record
12902  * Instances of this class encapsulate both record <em>definition</em> information, and record
12903  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12904  * to access Records cached in an {@link Roo.data.Store} object.<br>
12905  * <p>
12906  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12907  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12908  * objects.<br>
12909  * <p>
12910  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12911  * @constructor
12912  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12913  * {@link #create}. The parameters are the same.
12914  * @param {Array} data An associative Array of data values keyed by the field name.
12915  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12916  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12917  * not specified an integer id is generated.
12918  */
12919 Roo.data.Record = function(data, id){
12920     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12921     this.data = data;
12922 };
12923
12924 /**
12925  * Generate a constructor for a specific record layout.
12926  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12927  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12928  * Each field definition object may contain the following properties: <ul>
12929  * <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,
12930  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12931  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12932  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12933  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12934  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12935  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12936  * this may be omitted.</p></li>
12937  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12938  * <ul><li>auto (Default, implies no conversion)</li>
12939  * <li>string</li>
12940  * <li>int</li>
12941  * <li>float</li>
12942  * <li>boolean</li>
12943  * <li>date</li></ul></p></li>
12944  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12945  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12946  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12947  * by the Reader into an object that will be stored in the Record. It is passed the
12948  * following parameters:<ul>
12949  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12950  * </ul></p></li>
12951  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12952  * </ul>
12953  * <br>usage:<br><pre><code>
12954 var TopicRecord = Roo.data.Record.create(
12955     {name: 'title', mapping: 'topic_title'},
12956     {name: 'author', mapping: 'username'},
12957     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12958     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12959     {name: 'lastPoster', mapping: 'user2'},
12960     {name: 'excerpt', mapping: 'post_text'}
12961 );
12962
12963 var myNewRecord = new TopicRecord({
12964     title: 'Do my job please',
12965     author: 'noobie',
12966     totalPosts: 1,
12967     lastPost: new Date(),
12968     lastPoster: 'Animal',
12969     excerpt: 'No way dude!'
12970 });
12971 myStore.add(myNewRecord);
12972 </code></pre>
12973  * @method create
12974  * @static
12975  */
12976 Roo.data.Record.create = function(o){
12977     var f = function(){
12978         f.superclass.constructor.apply(this, arguments);
12979     };
12980     Roo.extend(f, Roo.data.Record);
12981     var p = f.prototype;
12982     p.fields = new Roo.util.MixedCollection(false, function(field){
12983         return field.name;
12984     });
12985     for(var i = 0, len = o.length; i < len; i++){
12986         p.fields.add(new Roo.data.Field(o[i]));
12987     }
12988     f.getField = function(name){
12989         return p.fields.get(name);  
12990     };
12991     return f;
12992 };
12993
12994 Roo.data.Record.AUTO_ID = 1000;
12995 Roo.data.Record.EDIT = 'edit';
12996 Roo.data.Record.REJECT = 'reject';
12997 Roo.data.Record.COMMIT = 'commit';
12998
12999 Roo.data.Record.prototype = {
13000     /**
13001      * Readonly flag - true if this record has been modified.
13002      * @type Boolean
13003      */
13004     dirty : false,
13005     editing : false,
13006     error: null,
13007     modified: null,
13008
13009     // private
13010     join : function(store){
13011         this.store = store;
13012     },
13013
13014     /**
13015      * Set the named field to the specified value.
13016      * @param {String} name The name of the field to set.
13017      * @param {Object} value The value to set the field to.
13018      */
13019     set : function(name, value){
13020         if(this.data[name] == value){
13021             return;
13022         }
13023         this.dirty = true;
13024         if(!this.modified){
13025             this.modified = {};
13026         }
13027         if(typeof this.modified[name] == 'undefined'){
13028             this.modified[name] = this.data[name];
13029         }
13030         this.data[name] = value;
13031         if(!this.editing && this.store){
13032             this.store.afterEdit(this);
13033         }       
13034     },
13035
13036     /**
13037      * Get the value of the named field.
13038      * @param {String} name The name of the field to get the value of.
13039      * @return {Object} The value of the field.
13040      */
13041     get : function(name){
13042         return this.data[name]; 
13043     },
13044
13045     // private
13046     beginEdit : function(){
13047         this.editing = true;
13048         this.modified = {}; 
13049     },
13050
13051     // private
13052     cancelEdit : function(){
13053         this.editing = false;
13054         delete this.modified;
13055     },
13056
13057     // private
13058     endEdit : function(){
13059         this.editing = false;
13060         if(this.dirty && this.store){
13061             this.store.afterEdit(this);
13062         }
13063     },
13064
13065     /**
13066      * Usually called by the {@link Roo.data.Store} which owns the Record.
13067      * Rejects all changes made to the Record since either creation, or the last commit operation.
13068      * Modified fields are reverted to their original values.
13069      * <p>
13070      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13071      * of reject operations.
13072      */
13073     reject : function(){
13074         var m = this.modified;
13075         for(var n in m){
13076             if(typeof m[n] != "function"){
13077                 this.data[n] = m[n];
13078             }
13079         }
13080         this.dirty = false;
13081         delete this.modified;
13082         this.editing = false;
13083         if(this.store){
13084             this.store.afterReject(this);
13085         }
13086     },
13087
13088     /**
13089      * Usually called by the {@link Roo.data.Store} which owns the Record.
13090      * Commits all changes made to the Record since either creation, or the last commit operation.
13091      * <p>
13092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13093      * of commit operations.
13094      */
13095     commit : function(){
13096         this.dirty = false;
13097         delete this.modified;
13098         this.editing = false;
13099         if(this.store){
13100             this.store.afterCommit(this);
13101         }
13102     },
13103
13104     // private
13105     hasError : function(){
13106         return this.error != null;
13107     },
13108
13109     // private
13110     clearError : function(){
13111         this.error = null;
13112     },
13113
13114     /**
13115      * Creates a copy of this record.
13116      * @param {String} id (optional) A new record id if you don't want to use this record's id
13117      * @return {Record}
13118      */
13119     copy : function(newId) {
13120         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13121     }
13122 };/*
13123  * Based on:
13124  * Ext JS Library 1.1.1
13125  * Copyright(c) 2006-2007, Ext JS, LLC.
13126  *
13127  * Originally Released Under LGPL - original licence link has changed is not relivant.
13128  *
13129  * Fork - LGPL
13130  * <script type="text/javascript">
13131  */
13132
13133
13134
13135 /**
13136  * @class Roo.data.Store
13137  * @extends Roo.util.Observable
13138  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13139  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13140  * <p>
13141  * 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
13142  * has no knowledge of the format of the data returned by the Proxy.<br>
13143  * <p>
13144  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13145  * instances from the data object. These records are cached and made available through accessor functions.
13146  * @constructor
13147  * Creates a new Store.
13148  * @param {Object} config A config object containing the objects needed for the Store to access data,
13149  * and read the data into Records.
13150  */
13151 Roo.data.Store = function(config){
13152     this.data = new Roo.util.MixedCollection(false);
13153     this.data.getKey = function(o){
13154         return o.id;
13155     };
13156     this.baseParams = {};
13157     // private
13158     this.paramNames = {
13159         "start" : "start",
13160         "limit" : "limit",
13161         "sort" : "sort",
13162         "dir" : "dir",
13163         "multisort" : "_multisort"
13164     };
13165
13166     if(config && config.data){
13167         this.inlineData = config.data;
13168         delete config.data;
13169     }
13170
13171     Roo.apply(this, config);
13172     
13173     if(this.reader){ // reader passed
13174         this.reader = Roo.factory(this.reader, Roo.data);
13175         this.reader.xmodule = this.xmodule || false;
13176         if(!this.recordType){
13177             this.recordType = this.reader.recordType;
13178         }
13179         if(this.reader.onMetaChange){
13180             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13181         }
13182     }
13183
13184     if(this.recordType){
13185         this.fields = this.recordType.prototype.fields;
13186     }
13187     this.modified = [];
13188
13189     this.addEvents({
13190         /**
13191          * @event datachanged
13192          * Fires when the data cache has changed, and a widget which is using this Store
13193          * as a Record cache should refresh its view.
13194          * @param {Store} this
13195          */
13196         datachanged : true,
13197         /**
13198          * @event metachange
13199          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13200          * @param {Store} this
13201          * @param {Object} meta The JSON metadata
13202          */
13203         metachange : true,
13204         /**
13205          * @event add
13206          * Fires when Records have been added to the Store
13207          * @param {Store} this
13208          * @param {Roo.data.Record[]} records The array of Records added
13209          * @param {Number} index The index at which the record(s) were added
13210          */
13211         add : true,
13212         /**
13213          * @event remove
13214          * Fires when a Record has been removed from the Store
13215          * @param {Store} this
13216          * @param {Roo.data.Record} record The Record that was removed
13217          * @param {Number} index The index at which the record was removed
13218          */
13219         remove : true,
13220         /**
13221          * @event update
13222          * Fires when a Record has been updated
13223          * @param {Store} this
13224          * @param {Roo.data.Record} record The Record that was updated
13225          * @param {String} operation The update operation being performed.  Value may be one of:
13226          * <pre><code>
13227  Roo.data.Record.EDIT
13228  Roo.data.Record.REJECT
13229  Roo.data.Record.COMMIT
13230          * </code></pre>
13231          */
13232         update : true,
13233         /**
13234          * @event clear
13235          * Fires when the data cache has been cleared.
13236          * @param {Store} this
13237          */
13238         clear : true,
13239         /**
13240          * @event beforeload
13241          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13242          * the load action will be canceled.
13243          * @param {Store} this
13244          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13245          */
13246         beforeload : true,
13247         /**
13248          * @event beforeloadadd
13249          * Fires after a new set of Records has been loaded.
13250          * @param {Store} this
13251          * @param {Roo.data.Record[]} records The Records that were loaded
13252          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13253          */
13254         beforeloadadd : true,
13255         /**
13256          * @event load
13257          * Fires after a new set of Records has been loaded, before they are added to the store.
13258          * @param {Store} this
13259          * @param {Roo.data.Record[]} records The Records that were loaded
13260          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13261          * @params {Object} return from reader
13262          */
13263         load : true,
13264         /**
13265          * @event loadexception
13266          * Fires if an exception occurs in the Proxy during loading.
13267          * Called with the signature of the Proxy's "loadexception" event.
13268          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13269          * 
13270          * @param {Proxy} 
13271          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13272          * @param {Object} load options 
13273          * @param {Object} jsonData from your request (normally this contains the Exception)
13274          */
13275         loadexception : true
13276     });
13277     
13278     if(this.proxy){
13279         this.proxy = Roo.factory(this.proxy, Roo.data);
13280         this.proxy.xmodule = this.xmodule || false;
13281         this.relayEvents(this.proxy,  ["loadexception"]);
13282     }
13283     this.sortToggle = {};
13284     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13285
13286     Roo.data.Store.superclass.constructor.call(this);
13287
13288     if(this.inlineData){
13289         this.loadData(this.inlineData);
13290         delete this.inlineData;
13291     }
13292 };
13293
13294 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13295      /**
13296     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13297     * without a remote query - used by combo/forms at present.
13298     */
13299     
13300     /**
13301     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13302     */
13303     /**
13304     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13305     */
13306     /**
13307     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13308     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13309     */
13310     /**
13311     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13312     * on any HTTP request
13313     */
13314     /**
13315     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13316     */
13317     /**
13318     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13319     */
13320     multiSort: false,
13321     /**
13322     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13323     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13324     */
13325     remoteSort : false,
13326
13327     /**
13328     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13329      * loaded or when a record is removed. (defaults to false).
13330     */
13331     pruneModifiedRecords : false,
13332
13333     // private
13334     lastOptions : null,
13335
13336     /**
13337      * Add Records to the Store and fires the add event.
13338      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13339      */
13340     add : function(records){
13341         records = [].concat(records);
13342         for(var i = 0, len = records.length; i < len; i++){
13343             records[i].join(this);
13344         }
13345         var index = this.data.length;
13346         this.data.addAll(records);
13347         this.fireEvent("add", this, records, index);
13348     },
13349
13350     /**
13351      * Remove a Record from the Store and fires the remove event.
13352      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13353      */
13354     remove : function(record){
13355         var index = this.data.indexOf(record);
13356         this.data.removeAt(index);
13357  
13358         if(this.pruneModifiedRecords){
13359             this.modified.remove(record);
13360         }
13361         this.fireEvent("remove", this, record, index);
13362     },
13363
13364     /**
13365      * Remove all Records from the Store and fires the clear event.
13366      */
13367     removeAll : function(){
13368         this.data.clear();
13369         if(this.pruneModifiedRecords){
13370             this.modified = [];
13371         }
13372         this.fireEvent("clear", this);
13373     },
13374
13375     /**
13376      * Inserts Records to the Store at the given index and fires the add event.
13377      * @param {Number} index The start index at which to insert the passed Records.
13378      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13379      */
13380     insert : function(index, records){
13381         records = [].concat(records);
13382         for(var i = 0, len = records.length; i < len; i++){
13383             this.data.insert(index, records[i]);
13384             records[i].join(this);
13385         }
13386         this.fireEvent("add", this, records, index);
13387     },
13388
13389     /**
13390      * Get the index within the cache of the passed Record.
13391      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13392      * @return {Number} The index of the passed Record. Returns -1 if not found.
13393      */
13394     indexOf : function(record){
13395         return this.data.indexOf(record);
13396     },
13397
13398     /**
13399      * Get the index within the cache of the Record with the passed id.
13400      * @param {String} id The id of the Record to find.
13401      * @return {Number} The index of the Record. Returns -1 if not found.
13402      */
13403     indexOfId : function(id){
13404         return this.data.indexOfKey(id);
13405     },
13406
13407     /**
13408      * Get the Record with the specified id.
13409      * @param {String} id The id of the Record to find.
13410      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13411      */
13412     getById : function(id){
13413         return this.data.key(id);
13414     },
13415
13416     /**
13417      * Get the Record at the specified index.
13418      * @param {Number} index The index of the Record to find.
13419      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13420      */
13421     getAt : function(index){
13422         return this.data.itemAt(index);
13423     },
13424
13425     /**
13426      * Returns a range of Records between specified indices.
13427      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13428      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13429      * @return {Roo.data.Record[]} An array of Records
13430      */
13431     getRange : function(start, end){
13432         return this.data.getRange(start, end);
13433     },
13434
13435     // private
13436     storeOptions : function(o){
13437         o = Roo.apply({}, o);
13438         delete o.callback;
13439         delete o.scope;
13440         this.lastOptions = o;
13441     },
13442
13443     /**
13444      * Loads the Record cache from the configured Proxy using the configured Reader.
13445      * <p>
13446      * If using remote paging, then the first load call must specify the <em>start</em>
13447      * and <em>limit</em> properties in the options.params property to establish the initial
13448      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13449      * <p>
13450      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13451      * and this call will return before the new data has been loaded. Perform any post-processing
13452      * in a callback function, or in a "load" event handler.</strong>
13453      * <p>
13454      * @param {Object} options An object containing properties which control loading options:<ul>
13455      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13456      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13457      * passed the following arguments:<ul>
13458      * <li>r : Roo.data.Record[]</li>
13459      * <li>options: Options object from the load call</li>
13460      * <li>success: Boolean success indicator</li></ul></li>
13461      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13462      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13463      * </ul>
13464      */
13465     load : function(options){
13466         options = options || {};
13467         if(this.fireEvent("beforeload", this, options) !== false){
13468             this.storeOptions(options);
13469             var p = Roo.apply(options.params || {}, this.baseParams);
13470             // if meta was not loaded from remote source.. try requesting it.
13471             if (!this.reader.metaFromRemote) {
13472                 p._requestMeta = 1;
13473             }
13474             if(this.sortInfo && this.remoteSort){
13475                 var pn = this.paramNames;
13476                 p[pn["sort"]] = this.sortInfo.field;
13477                 p[pn["dir"]] = this.sortInfo.direction;
13478             }
13479             if (this.multiSort) {
13480                 var pn = this.paramNames;
13481                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13482             }
13483             
13484             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13485         }
13486     },
13487
13488     /**
13489      * Reloads the Record cache from the configured Proxy using the configured Reader and
13490      * the options from the last load operation performed.
13491      * @param {Object} options (optional) An object containing properties which may override the options
13492      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13493      * the most recently used options are reused).
13494      */
13495     reload : function(options){
13496         this.load(Roo.applyIf(options||{}, this.lastOptions));
13497     },
13498
13499     // private
13500     // Called as a callback by the Reader during a load operation.
13501     loadRecords : function(o, options, success){
13502         if(!o || success === false){
13503             if(success !== false){
13504                 this.fireEvent("load", this, [], options, o);
13505             }
13506             if(options.callback){
13507                 options.callback.call(options.scope || this, [], options, false);
13508             }
13509             return;
13510         }
13511         // if data returned failure - throw an exception.
13512         if (o.success === false) {
13513             // show a message if no listener is registered.
13514             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13515                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13516             }
13517             // loadmask wil be hooked into this..
13518             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13519             return;
13520         }
13521         var r = o.records, t = o.totalRecords || r.length;
13522         
13523         this.fireEvent("beforeloadadd", this, r, options, o);
13524         
13525         if(!options || options.add !== true){
13526             if(this.pruneModifiedRecords){
13527                 this.modified = [];
13528             }
13529             for(var i = 0, len = r.length; i < len; i++){
13530                 r[i].join(this);
13531             }
13532             if(this.snapshot){
13533                 this.data = this.snapshot;
13534                 delete this.snapshot;
13535             }
13536             this.data.clear();
13537             this.data.addAll(r);
13538             this.totalLength = t;
13539             this.applySort();
13540             this.fireEvent("datachanged", this);
13541         }else{
13542             this.totalLength = Math.max(t, this.data.length+r.length);
13543             this.add(r);
13544         }
13545         
13546         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13547                 
13548             var e = new Roo.data.Record({});
13549
13550             e.set(this.parent.displayField, this.parent.emptyTitle);
13551             e.set(this.parent.valueField, '');
13552
13553             this.insert(0, e);
13554         }
13555             
13556         this.fireEvent("load", this, r, options, o);
13557         if(options.callback){
13558             options.callback.call(options.scope || this, r, options, true);
13559         }
13560     },
13561
13562
13563     /**
13564      * Loads data from a passed data block. A Reader which understands the format of the data
13565      * must have been configured in the constructor.
13566      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13567      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13568      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13569      */
13570     loadData : function(o, append){
13571         var r = this.reader.readRecords(o);
13572         this.loadRecords(r, {add: append}, true);
13573     },
13574     
13575      /**
13576      * using 'cn' the nested child reader read the child array into it's child stores.
13577      * @param {Object} rec The record with a 'children array
13578      */
13579     loadDataFromChildren : function(rec)
13580     {
13581         this.loadData(this.reader.toLoadData(rec));
13582     },
13583     
13584
13585     /**
13586      * Gets the number of cached records.
13587      * <p>
13588      * <em>If using paging, this may not be the total size of the dataset. If the data object
13589      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13590      * the data set size</em>
13591      */
13592     getCount : function(){
13593         return this.data.length || 0;
13594     },
13595
13596     /**
13597      * Gets the total number of records in the dataset as returned by the server.
13598      * <p>
13599      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13600      * the dataset size</em>
13601      */
13602     getTotalCount : function(){
13603         return this.totalLength || 0;
13604     },
13605
13606     /**
13607      * Returns the sort state of the Store as an object with two properties:
13608      * <pre><code>
13609  field {String} The name of the field by which the Records are sorted
13610  direction {String} The sort order, "ASC" or "DESC"
13611      * </code></pre>
13612      */
13613     getSortState : function(){
13614         return this.sortInfo;
13615     },
13616
13617     // private
13618     applySort : function(){
13619         if(this.sortInfo && !this.remoteSort){
13620             var s = this.sortInfo, f = s.field;
13621             var st = this.fields.get(f).sortType;
13622             var fn = function(r1, r2){
13623                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13624                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13625             };
13626             this.data.sort(s.direction, fn);
13627             if(this.snapshot && this.snapshot != this.data){
13628                 this.snapshot.sort(s.direction, fn);
13629             }
13630         }
13631     },
13632
13633     /**
13634      * Sets the default sort column and order to be used by the next load operation.
13635      * @param {String} fieldName The name of the field to sort by.
13636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13637      */
13638     setDefaultSort : function(field, dir){
13639         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13640     },
13641
13642     /**
13643      * Sort the Records.
13644      * If remote sorting is used, the sort is performed on the server, and the cache is
13645      * reloaded. If local sorting is used, the cache is sorted internally.
13646      * @param {String} fieldName The name of the field to sort by.
13647      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13648      */
13649     sort : function(fieldName, dir){
13650         var f = this.fields.get(fieldName);
13651         if(!dir){
13652             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13653             
13654             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13655                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13656             }else{
13657                 dir = f.sortDir;
13658             }
13659         }
13660         this.sortToggle[f.name] = dir;
13661         this.sortInfo = {field: f.name, direction: dir};
13662         if(!this.remoteSort){
13663             this.applySort();
13664             this.fireEvent("datachanged", this);
13665         }else{
13666             this.load(this.lastOptions);
13667         }
13668     },
13669
13670     /**
13671      * Calls the specified function for each of the Records in the cache.
13672      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13673      * Returning <em>false</em> aborts and exits the iteration.
13674      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13675      */
13676     each : function(fn, scope){
13677         this.data.each(fn, scope);
13678     },
13679
13680     /**
13681      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13682      * (e.g., during paging).
13683      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13684      */
13685     getModifiedRecords : function(){
13686         return this.modified;
13687     },
13688
13689     // private
13690     createFilterFn : function(property, value, anyMatch){
13691         if(!value.exec){ // not a regex
13692             value = String(value);
13693             if(value.length == 0){
13694                 return false;
13695             }
13696             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13697         }
13698         return function(r){
13699             return value.test(r.data[property]);
13700         };
13701     },
13702
13703     /**
13704      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13705      * @param {String} property A field on your records
13706      * @param {Number} start The record index to start at (defaults to 0)
13707      * @param {Number} end The last record index to include (defaults to length - 1)
13708      * @return {Number} The sum
13709      */
13710     sum : function(property, start, end){
13711         var rs = this.data.items, v = 0;
13712         start = start || 0;
13713         end = (end || end === 0) ? end : rs.length-1;
13714
13715         for(var i = start; i <= end; i++){
13716             v += (rs[i].data[property] || 0);
13717         }
13718         return v;
13719     },
13720
13721     /**
13722      * Filter the records by a specified property.
13723      * @param {String} field A field on your records
13724      * @param {String/RegExp} value Either a string that the field
13725      * should start with or a RegExp to test against the field
13726      * @param {Boolean} anyMatch True to match any part not just the beginning
13727      */
13728     filter : function(property, value, anyMatch){
13729         var fn = this.createFilterFn(property, value, anyMatch);
13730         return fn ? this.filterBy(fn) : this.clearFilter();
13731     },
13732
13733     /**
13734      * Filter by a function. The specified function will be called with each
13735      * record in this data source. If the function returns true the record is included,
13736      * otherwise it is filtered.
13737      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13738      * @param {Object} scope (optional) The scope of the function (defaults to this)
13739      */
13740     filterBy : function(fn, scope){
13741         this.snapshot = this.snapshot || this.data;
13742         this.data = this.queryBy(fn, scope||this);
13743         this.fireEvent("datachanged", this);
13744     },
13745
13746     /**
13747      * Query the records by a specified property.
13748      * @param {String} field A field on your records
13749      * @param {String/RegExp} value Either a string that the field
13750      * should start with or a RegExp to test against the field
13751      * @param {Boolean} anyMatch True to match any part not just the beginning
13752      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13753      */
13754     query : function(property, value, anyMatch){
13755         var fn = this.createFilterFn(property, value, anyMatch);
13756         return fn ? this.queryBy(fn) : this.data.clone();
13757     },
13758
13759     /**
13760      * Query by a function. The specified function will be called with each
13761      * record in this data source. If the function returns true the record is included
13762      * in the results.
13763      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13764      * @param {Object} scope (optional) The scope of the function (defaults to this)
13765       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13766      **/
13767     queryBy : function(fn, scope){
13768         var data = this.snapshot || this.data;
13769         return data.filterBy(fn, scope||this);
13770     },
13771
13772     /**
13773      * Collects unique values for a particular dataIndex from this store.
13774      * @param {String} dataIndex The property to collect
13775      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13776      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13777      * @return {Array} An array of the unique values
13778      **/
13779     collect : function(dataIndex, allowNull, bypassFilter){
13780         var d = (bypassFilter === true && this.snapshot) ?
13781                 this.snapshot.items : this.data.items;
13782         var v, sv, r = [], l = {};
13783         for(var i = 0, len = d.length; i < len; i++){
13784             v = d[i].data[dataIndex];
13785             sv = String(v);
13786             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13787                 l[sv] = true;
13788                 r[r.length] = v;
13789             }
13790         }
13791         return r;
13792     },
13793
13794     /**
13795      * Revert to a view of the Record cache with no filtering applied.
13796      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13797      */
13798     clearFilter : function(suppressEvent){
13799         if(this.snapshot && this.snapshot != this.data){
13800             this.data = this.snapshot;
13801             delete this.snapshot;
13802             if(suppressEvent !== true){
13803                 this.fireEvent("datachanged", this);
13804             }
13805         }
13806     },
13807
13808     // private
13809     afterEdit : function(record){
13810         if(this.modified.indexOf(record) == -1){
13811             this.modified.push(record);
13812         }
13813         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13814     },
13815     
13816     // private
13817     afterReject : function(record){
13818         this.modified.remove(record);
13819         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13820     },
13821
13822     // private
13823     afterCommit : function(record){
13824         this.modified.remove(record);
13825         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13826     },
13827
13828     /**
13829      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13830      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13831      */
13832     commitChanges : function(){
13833         var m = this.modified.slice(0);
13834         this.modified = [];
13835         for(var i = 0, len = m.length; i < len; i++){
13836             m[i].commit();
13837         }
13838     },
13839
13840     /**
13841      * Cancel outstanding changes on all changed records.
13842      */
13843     rejectChanges : function(){
13844         var m = this.modified.slice(0);
13845         this.modified = [];
13846         for(var i = 0, len = m.length; i < len; i++){
13847             m[i].reject();
13848         }
13849     },
13850
13851     onMetaChange : function(meta, rtype, o){
13852         this.recordType = rtype;
13853         this.fields = rtype.prototype.fields;
13854         delete this.snapshot;
13855         this.sortInfo = meta.sortInfo || this.sortInfo;
13856         this.modified = [];
13857         this.fireEvent('metachange', this, this.reader.meta);
13858     },
13859     
13860     moveIndex : function(data, type)
13861     {
13862         var index = this.indexOf(data);
13863         
13864         var newIndex = index + type;
13865         
13866         this.remove(data);
13867         
13868         this.insert(newIndex, data);
13869         
13870     }
13871 });/*
13872  * Based on:
13873  * Ext JS Library 1.1.1
13874  * Copyright(c) 2006-2007, Ext JS, LLC.
13875  *
13876  * Originally Released Under LGPL - original licence link has changed is not relivant.
13877  *
13878  * Fork - LGPL
13879  * <script type="text/javascript">
13880  */
13881
13882 /**
13883  * @class Roo.data.SimpleStore
13884  * @extends Roo.data.Store
13885  * Small helper class to make creating Stores from Array data easier.
13886  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13887  * @cfg {Array} fields An array of field definition objects, or field name strings.
13888  * @cfg {Object} an existing reader (eg. copied from another store)
13889  * @cfg {Array} data The multi-dimensional array of data
13890  * @constructor
13891  * @param {Object} config
13892  */
13893 Roo.data.SimpleStore = function(config)
13894 {
13895     Roo.data.SimpleStore.superclass.constructor.call(this, {
13896         isLocal : true,
13897         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13898                 id: config.id
13899             },
13900             Roo.data.Record.create(config.fields)
13901         ),
13902         proxy : new Roo.data.MemoryProxy(config.data)
13903     });
13904     this.load();
13905 };
13906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917 /**
13918 /**
13919  * @extends Roo.data.Store
13920  * @class Roo.data.JsonStore
13921  * Small helper class to make creating Stores for JSON data easier. <br/>
13922 <pre><code>
13923 var store = new Roo.data.JsonStore({
13924     url: 'get-images.php',
13925     root: 'images',
13926     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13927 });
13928 </code></pre>
13929  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13930  * JsonReader and HttpProxy (unless inline data is provided).</b>
13931  * @cfg {Array} fields An array of field definition objects, or field name strings.
13932  * @constructor
13933  * @param {Object} config
13934  */
13935 Roo.data.JsonStore = function(c){
13936     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13937         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13938         reader: new Roo.data.JsonReader(c, c.fields)
13939     }));
13940 };
13941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13942  * Based on:
13943  * Ext JS Library 1.1.1
13944  * Copyright(c) 2006-2007, Ext JS, LLC.
13945  *
13946  * Originally Released Under LGPL - original licence link has changed is not relivant.
13947  *
13948  * Fork - LGPL
13949  * <script type="text/javascript">
13950  */
13951
13952  
13953 Roo.data.Field = function(config){
13954     if(typeof config == "string"){
13955         config = {name: config};
13956     }
13957     Roo.apply(this, config);
13958     
13959     if(!this.type){
13960         this.type = "auto";
13961     }
13962     
13963     var st = Roo.data.SortTypes;
13964     // named sortTypes are supported, here we look them up
13965     if(typeof this.sortType == "string"){
13966         this.sortType = st[this.sortType];
13967     }
13968     
13969     // set default sortType for strings and dates
13970     if(!this.sortType){
13971         switch(this.type){
13972             case "string":
13973                 this.sortType = st.asUCString;
13974                 break;
13975             case "date":
13976                 this.sortType = st.asDate;
13977                 break;
13978             default:
13979                 this.sortType = st.none;
13980         }
13981     }
13982
13983     // define once
13984     var stripRe = /[\$,%]/g;
13985
13986     // prebuilt conversion function for this field, instead of
13987     // switching every time we're reading a value
13988     if(!this.convert){
13989         var cv, dateFormat = this.dateFormat;
13990         switch(this.type){
13991             case "":
13992             case "auto":
13993             case undefined:
13994                 cv = function(v){ return v; };
13995                 break;
13996             case "string":
13997                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13998                 break;
13999             case "int":
14000                 cv = function(v){
14001                     return v !== undefined && v !== null && v !== '' ?
14002                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14003                     };
14004                 break;
14005             case "float":
14006                 cv = function(v){
14007                     return v !== undefined && v !== null && v !== '' ?
14008                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14009                     };
14010                 break;
14011             case "bool":
14012             case "boolean":
14013                 cv = function(v){ return v === true || v === "true" || v == 1; };
14014                 break;
14015             case "date":
14016                 cv = function(v){
14017                     if(!v){
14018                         return '';
14019                     }
14020                     if(v instanceof Date){
14021                         return v;
14022                     }
14023                     if(dateFormat){
14024                         if(dateFormat == "timestamp"){
14025                             return new Date(v*1000);
14026                         }
14027                         return Date.parseDate(v, dateFormat);
14028                     }
14029                     var parsed = Date.parse(v);
14030                     return parsed ? new Date(parsed) : null;
14031                 };
14032              break;
14033             
14034         }
14035         this.convert = cv;
14036     }
14037 };
14038
14039 Roo.data.Field.prototype = {
14040     dateFormat: null,
14041     defaultValue: "",
14042     mapping: null,
14043     sortType : null,
14044     sortDir : "ASC"
14045 };/*
14046  * Based on:
14047  * Ext JS Library 1.1.1
14048  * Copyright(c) 2006-2007, Ext JS, LLC.
14049  *
14050  * Originally Released Under LGPL - original licence link has changed is not relivant.
14051  *
14052  * Fork - LGPL
14053  * <script type="text/javascript">
14054  */
14055  
14056 // Base class for reading structured data from a data source.  This class is intended to be
14057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14058
14059 /**
14060  * @class Roo.data.DataReader
14061  * Base class for reading structured data from a data source.  This class is intended to be
14062  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14063  */
14064
14065 Roo.data.DataReader = function(meta, recordType){
14066     
14067     this.meta = meta;
14068     
14069     this.recordType = recordType instanceof Array ? 
14070         Roo.data.Record.create(recordType) : recordType;
14071 };
14072
14073 Roo.data.DataReader.prototype = {
14074     
14075     
14076     readerType : 'Data',
14077      /**
14078      * Create an empty record
14079      * @param {Object} data (optional) - overlay some values
14080      * @return {Roo.data.Record} record created.
14081      */
14082     newRow :  function(d) {
14083         var da =  {};
14084         this.recordType.prototype.fields.each(function(c) {
14085             switch( c.type) {
14086                 case 'int' : da[c.name] = 0; break;
14087                 case 'date' : da[c.name] = new Date(); break;
14088                 case 'float' : da[c.name] = 0.0; break;
14089                 case 'boolean' : da[c.name] = false; break;
14090                 default : da[c.name] = ""; break;
14091             }
14092             
14093         });
14094         return new this.recordType(Roo.apply(da, d));
14095     }
14096     
14097     
14098 };/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108
14109 /**
14110  * @class Roo.data.DataProxy
14111  * @extends Roo.data.Observable
14112  * This class is an abstract base class for implementations which provide retrieval of
14113  * unformatted data objects.<br>
14114  * <p>
14115  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14116  * (of the appropriate type which knows how to parse the data object) to provide a block of
14117  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14118  * <p>
14119  * Custom implementations must implement the load method as described in
14120  * {@link Roo.data.HttpProxy#load}.
14121  */
14122 Roo.data.DataProxy = function(){
14123     this.addEvents({
14124         /**
14125          * @event beforeload
14126          * Fires before a network request is made to retrieve a data object.
14127          * @param {Object} This DataProxy object.
14128          * @param {Object} params The params parameter to the load function.
14129          */
14130         beforeload : true,
14131         /**
14132          * @event load
14133          * Fires before the load method's callback is called.
14134          * @param {Object} This DataProxy object.
14135          * @param {Object} o The data object.
14136          * @param {Object} arg The callback argument object passed to the load function.
14137          */
14138         load : true,
14139         /**
14140          * @event loadexception
14141          * Fires if an Exception occurs during data retrieval.
14142          * @param {Object} This DataProxy object.
14143          * @param {Object} o The data object.
14144          * @param {Object} arg The callback argument object passed to the load function.
14145          * @param {Object} e The Exception.
14146          */
14147         loadexception : true
14148     });
14149     Roo.data.DataProxy.superclass.constructor.call(this);
14150 };
14151
14152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14153
14154     /**
14155      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14156      */
14157 /*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167 /**
14168  * @class Roo.data.MemoryProxy
14169  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14170  * to the Reader when its load method is called.
14171  * @constructor
14172  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14173  */
14174 Roo.data.MemoryProxy = function(data){
14175     if (data.data) {
14176         data = data.data;
14177     }
14178     Roo.data.MemoryProxy.superclass.constructor.call(this);
14179     this.data = data;
14180 };
14181
14182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14183     
14184     /**
14185      * Load data from the requested source (in this case an in-memory
14186      * data object passed to the constructor), read the data object into
14187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14188      * process that block using the passed callback.
14189      * @param {Object} params This parameter is not used by the MemoryProxy class.
14190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14191      * object into a block of Roo.data.Records.
14192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14193      * The function must be passed <ul>
14194      * <li>The Record block object</li>
14195      * <li>The "arg" argument from the load function</li>
14196      * <li>A boolean success indicator</li>
14197      * </ul>
14198      * @param {Object} scope The scope in which to call the callback
14199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14200      */
14201     load : function(params, reader, callback, scope, arg){
14202         params = params || {};
14203         var result;
14204         try {
14205             result = reader.readRecords(params.data ? params.data :this.data);
14206         }catch(e){
14207             this.fireEvent("loadexception", this, arg, null, e);
14208             callback.call(scope, null, arg, false);
14209             return;
14210         }
14211         callback.call(scope, result, arg, true);
14212     },
14213     
14214     // private
14215     update : function(params, records){
14216         
14217     }
14218 });/*
14219  * Based on:
14220  * Ext JS Library 1.1.1
14221  * Copyright(c) 2006-2007, Ext JS, LLC.
14222  *
14223  * Originally Released Under LGPL - original licence link has changed is not relivant.
14224  *
14225  * Fork - LGPL
14226  * <script type="text/javascript">
14227  */
14228 /**
14229  * @class Roo.data.HttpProxy
14230  * @extends Roo.data.DataProxy
14231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14232  * configured to reference a certain URL.<br><br>
14233  * <p>
14234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14235  * from which the running page was served.<br><br>
14236  * <p>
14237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14238  * <p>
14239  * Be aware that to enable the browser to parse an XML document, the server must set
14240  * the Content-Type header in the HTTP response to "text/xml".
14241  * @constructor
14242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14244  * will be used to make the request.
14245  */
14246 Roo.data.HttpProxy = function(conn){
14247     Roo.data.HttpProxy.superclass.constructor.call(this);
14248     // is conn a conn config or a real conn?
14249     this.conn = conn;
14250     this.useAjax = !conn || !conn.events;
14251   
14252 };
14253
14254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14255     // thse are take from connection...
14256     
14257     /**
14258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14259      */
14260     /**
14261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14262      * extra parameters to each request made by this object. (defaults to undefined)
14263      */
14264     /**
14265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14266      *  to each request made by this object. (defaults to undefined)
14267      */
14268     /**
14269      * @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)
14270      */
14271     /**
14272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14273      */
14274      /**
14275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14276      * @type Boolean
14277      */
14278   
14279
14280     /**
14281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14282      * @type Boolean
14283      */
14284     /**
14285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14287      * a finer-grained basis than the DataProxy events.
14288      */
14289     getConnection : function(){
14290         return this.useAjax ? Roo.Ajax : this.conn;
14291     },
14292
14293     /**
14294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14296      * process that block using the passed callback.
14297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14298      * for the request to the remote server.
14299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14300      * object into a block of Roo.data.Records.
14301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14302      * The function must be passed <ul>
14303      * <li>The Record block object</li>
14304      * <li>The "arg" argument from the load function</li>
14305      * <li>A boolean success indicator</li>
14306      * </ul>
14307      * @param {Object} scope The scope in which to call the callback
14308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14309      */
14310     load : function(params, reader, callback, scope, arg){
14311         if(this.fireEvent("beforeload", this, params) !== false){
14312             var  o = {
14313                 params : params || {},
14314                 request: {
14315                     callback : callback,
14316                     scope : scope,
14317                     arg : arg
14318                 },
14319                 reader: reader,
14320                 callback : this.loadResponse,
14321                 scope: this
14322             };
14323             if(this.useAjax){
14324                 Roo.applyIf(o, this.conn);
14325                 if(this.activeRequest){
14326                     Roo.Ajax.abort(this.activeRequest);
14327                 }
14328                 this.activeRequest = Roo.Ajax.request(o);
14329             }else{
14330                 this.conn.request(o);
14331             }
14332         }else{
14333             callback.call(scope||this, null, arg, false);
14334         }
14335     },
14336
14337     // private
14338     loadResponse : function(o, success, response){
14339         delete this.activeRequest;
14340         if(!success){
14341             this.fireEvent("loadexception", this, o, response);
14342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14343             return;
14344         }
14345         var result;
14346         try {
14347             result = o.reader.read(response);
14348         }catch(e){
14349             this.fireEvent("loadexception", this, o, response, e);
14350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14351             return;
14352         }
14353         
14354         this.fireEvent("load", this, o, o.request.arg);
14355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14356     },
14357
14358     // private
14359     update : function(dataSet){
14360
14361     },
14362
14363     // private
14364     updateResponse : function(dataSet){
14365
14366     }
14367 });/*
14368  * Based on:
14369  * Ext JS Library 1.1.1
14370  * Copyright(c) 2006-2007, Ext JS, LLC.
14371  *
14372  * Originally Released Under LGPL - original licence link has changed is not relivant.
14373  *
14374  * Fork - LGPL
14375  * <script type="text/javascript">
14376  */
14377
14378 /**
14379  * @class Roo.data.ScriptTagProxy
14380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14381  * other than the originating domain of the running page.<br><br>
14382  * <p>
14383  * <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
14384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14385  * <p>
14386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14387  * source code that is used as the source inside a &lt;script> tag.<br><br>
14388  * <p>
14389  * In order for the browser to process the returned data, the server must wrap the data object
14390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14392  * depending on whether the callback name was passed:
14393  * <p>
14394  * <pre><code>
14395 boolean scriptTag = false;
14396 String cb = request.getParameter("callback");
14397 if (cb != null) {
14398     scriptTag = true;
14399     response.setContentType("text/javascript");
14400 } else {
14401     response.setContentType("application/x-json");
14402 }
14403 Writer out = response.getWriter();
14404 if (scriptTag) {
14405     out.write(cb + "(");
14406 }
14407 out.print(dataBlock.toJsonString());
14408 if (scriptTag) {
14409     out.write(");");
14410 }
14411 </pre></code>
14412  *
14413  * @constructor
14414  * @param {Object} config A configuration object.
14415  */
14416 Roo.data.ScriptTagProxy = function(config){
14417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14418     Roo.apply(this, config);
14419     this.head = document.getElementsByTagName("head")[0];
14420 };
14421
14422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14423
14424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14425     /**
14426      * @cfg {String} url The URL from which to request the data object.
14427      */
14428     /**
14429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14430      */
14431     timeout : 30000,
14432     /**
14433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14434      * the server the name of the callback function set up by the load call to process the returned data object.
14435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14436      * javascript output which calls this named function passing the data object as its only parameter.
14437      */
14438     callbackParam : "callback",
14439     /**
14440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14441      * name to the request.
14442      */
14443     nocache : true,
14444
14445     /**
14446      * Load data from the configured URL, read the data object into
14447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14448      * process that block using the passed callback.
14449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14450      * for the request to the remote server.
14451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14452      * object into a block of Roo.data.Records.
14453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14454      * The function must be passed <ul>
14455      * <li>The Record block object</li>
14456      * <li>The "arg" argument from the load function</li>
14457      * <li>A boolean success indicator</li>
14458      * </ul>
14459      * @param {Object} scope The scope in which to call the callback
14460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14461      */
14462     load : function(params, reader, callback, scope, arg){
14463         if(this.fireEvent("beforeload", this, params) !== false){
14464
14465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14466
14467             var url = this.url;
14468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14469             if(this.nocache){
14470                 url += "&_dc=" + (new Date().getTime());
14471             }
14472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14473             var trans = {
14474                 id : transId,
14475                 cb : "stcCallback"+transId,
14476                 scriptId : "stcScript"+transId,
14477                 params : params,
14478                 arg : arg,
14479                 url : url,
14480                 callback : callback,
14481                 scope : scope,
14482                 reader : reader
14483             };
14484             var conn = this;
14485
14486             window[trans.cb] = function(o){
14487                 conn.handleResponse(o, trans);
14488             };
14489
14490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14491
14492             if(this.autoAbort !== false){
14493                 this.abort();
14494             }
14495
14496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14497
14498             var script = document.createElement("script");
14499             script.setAttribute("src", url);
14500             script.setAttribute("type", "text/javascript");
14501             script.setAttribute("id", trans.scriptId);
14502             this.head.appendChild(script);
14503
14504             this.trans = trans;
14505         }else{
14506             callback.call(scope||this, null, arg, false);
14507         }
14508     },
14509
14510     // private
14511     isLoading : function(){
14512         return this.trans ? true : false;
14513     },
14514
14515     /**
14516      * Abort the current server request.
14517      */
14518     abort : function(){
14519         if(this.isLoading()){
14520             this.destroyTrans(this.trans);
14521         }
14522     },
14523
14524     // private
14525     destroyTrans : function(trans, isLoaded){
14526         this.head.removeChild(document.getElementById(trans.scriptId));
14527         clearTimeout(trans.timeoutId);
14528         if(isLoaded){
14529             window[trans.cb] = undefined;
14530             try{
14531                 delete window[trans.cb];
14532             }catch(e){}
14533         }else{
14534             // if hasn't been loaded, wait for load to remove it to prevent script error
14535             window[trans.cb] = function(){
14536                 window[trans.cb] = undefined;
14537                 try{
14538                     delete window[trans.cb];
14539                 }catch(e){}
14540             };
14541         }
14542     },
14543
14544     // private
14545     handleResponse : function(o, trans){
14546         this.trans = false;
14547         this.destroyTrans(trans, true);
14548         var result;
14549         try {
14550             result = trans.reader.readRecords(o);
14551         }catch(e){
14552             this.fireEvent("loadexception", this, o, trans.arg, e);
14553             trans.callback.call(trans.scope||window, null, trans.arg, false);
14554             return;
14555         }
14556         this.fireEvent("load", this, o, trans.arg);
14557         trans.callback.call(trans.scope||window, result, trans.arg, true);
14558     },
14559
14560     // private
14561     handleFailure : function(trans){
14562         this.trans = false;
14563         this.destroyTrans(trans, false);
14564         this.fireEvent("loadexception", this, null, trans.arg);
14565         trans.callback.call(trans.scope||window, null, trans.arg, false);
14566     }
14567 });/*
14568  * Based on:
14569  * Ext JS Library 1.1.1
14570  * Copyright(c) 2006-2007, Ext JS, LLC.
14571  *
14572  * Originally Released Under LGPL - original licence link has changed is not relivant.
14573  *
14574  * Fork - LGPL
14575  * <script type="text/javascript">
14576  */
14577
14578 /**
14579  * @class Roo.data.JsonReader
14580  * @extends Roo.data.DataReader
14581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14582  * based on mappings in a provided Roo.data.Record constructor.
14583  * 
14584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14585  * in the reply previously. 
14586  * 
14587  * <p>
14588  * Example code:
14589  * <pre><code>
14590 var RecordDef = Roo.data.Record.create([
14591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14593 ]);
14594 var myReader = new Roo.data.JsonReader({
14595     totalProperty: "results",    // The property which contains the total dataset size (optional)
14596     root: "rows",                // The property which contains an Array of row objects
14597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14598 }, RecordDef);
14599 </code></pre>
14600  * <p>
14601  * This would consume a JSON file like this:
14602  * <pre><code>
14603 { 'results': 2, 'rows': [
14604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14606 }
14607 </code></pre>
14608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14610  * paged from the remote server.
14611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14612  * @cfg {String} root name of the property which contains the Array of row objects.
14613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14614  * @cfg {Array} fields Array of field definition objects
14615  * @constructor
14616  * Create a new JsonReader
14617  * @param {Object} meta Metadata configuration options
14618  * @param {Object} recordType Either an Array of field definition objects,
14619  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14620  */
14621 Roo.data.JsonReader = function(meta, recordType){
14622     
14623     meta = meta || {};
14624     // set some defaults:
14625     Roo.applyIf(meta, {
14626         totalProperty: 'total',
14627         successProperty : 'success',
14628         root : 'data',
14629         id : 'id'
14630     });
14631     
14632     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14633 };
14634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14635     
14636     readerType : 'Json',
14637     
14638     /**
14639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14640      * Used by Store query builder to append _requestMeta to params.
14641      * 
14642      */
14643     metaFromRemote : false,
14644     /**
14645      * This method is only used by a DataProxy which has retrieved data from a remote server.
14646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14647      * @return {Object} data A data block which is used by an Roo.data.Store object as
14648      * a cache of Roo.data.Records.
14649      */
14650     read : function(response){
14651         var json = response.responseText;
14652        
14653         var o = /* eval:var:o */ eval("("+json+")");
14654         if(!o) {
14655             throw {message: "JsonReader.read: Json object not found"};
14656         }
14657         
14658         if(o.metaData){
14659             
14660             delete this.ef;
14661             this.metaFromRemote = true;
14662             this.meta = o.metaData;
14663             this.recordType = Roo.data.Record.create(o.metaData.fields);
14664             this.onMetaChange(this.meta, this.recordType, o);
14665         }
14666         return this.readRecords(o);
14667     },
14668
14669     // private function a store will implement
14670     onMetaChange : function(meta, recordType, o){
14671
14672     },
14673
14674     /**
14675          * @ignore
14676          */
14677     simpleAccess: function(obj, subsc) {
14678         return obj[subsc];
14679     },
14680
14681         /**
14682          * @ignore
14683          */
14684     getJsonAccessor: function(){
14685         var re = /[\[\.]/;
14686         return function(expr) {
14687             try {
14688                 return(re.test(expr))
14689                     ? new Function("obj", "return obj." + expr)
14690                     : function(obj){
14691                         return obj[expr];
14692                     };
14693             } catch(e){}
14694             return Roo.emptyFn;
14695         };
14696     }(),
14697
14698     /**
14699      * Create a data block containing Roo.data.Records from an XML document.
14700      * @param {Object} o An object which contains an Array of row objects in the property specified
14701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14702      * which contains the total size of the dataset.
14703      * @return {Object} data A data block which is used by an Roo.data.Store object as
14704      * a cache of Roo.data.Records.
14705      */
14706     readRecords : function(o){
14707         /**
14708          * After any data loads, the raw JSON data is available for further custom processing.
14709          * @type Object
14710          */
14711         this.o = o;
14712         var s = this.meta, Record = this.recordType,
14713             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14714
14715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14716         if (!this.ef) {
14717             if(s.totalProperty) {
14718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14719                 }
14720                 if(s.successProperty) {
14721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14722                 }
14723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14724                 if (s.id) {
14725                         var g = this.getJsonAccessor(s.id);
14726                         this.getId = function(rec) {
14727                                 var r = g(rec);  
14728                                 return (r === undefined || r === "") ? null : r;
14729                         };
14730                 } else {
14731                         this.getId = function(){return null;};
14732                 }
14733             this.ef = [];
14734             for(var jj = 0; jj < fl; jj++){
14735                 f = fi[jj];
14736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14737                 this.ef[jj] = this.getJsonAccessor(map);
14738             }
14739         }
14740
14741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14742         if(s.totalProperty){
14743             var vt = parseInt(this.getTotal(o), 10);
14744             if(!isNaN(vt)){
14745                 totalRecords = vt;
14746             }
14747         }
14748         if(s.successProperty){
14749             var vs = this.getSuccess(o);
14750             if(vs === false || vs === 'false'){
14751                 success = false;
14752             }
14753         }
14754         var records = [];
14755         for(var i = 0; i < c; i++){
14756                 var n = root[i];
14757             var values = {};
14758             var id = this.getId(n);
14759             for(var j = 0; j < fl; j++){
14760                 f = fi[j];
14761             var v = this.ef[j](n);
14762             if (!f.convert) {
14763                 Roo.log('missing convert for ' + f.name);
14764                 Roo.log(f);
14765                 continue;
14766             }
14767             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14768             }
14769             var record = new Record(values, id);
14770             record.json = n;
14771             records[i] = record;
14772         }
14773         return {
14774             raw : o,
14775             success : success,
14776             records : records,
14777             totalRecords : totalRecords
14778         };
14779     },
14780     // used when loading children.. @see loadDataFromChildren
14781     toLoadData: function(rec)
14782     {
14783         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14784         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14785         return { data : data, total : data.length };
14786         
14787     }
14788 });/*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798
14799 /**
14800  * @class Roo.data.ArrayReader
14801  * @extends Roo.data.DataReader
14802  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14803  * Each element of that Array represents a row of data fields. The
14804  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14805  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14806  * <p>
14807  * Example code:.
14808  * <pre><code>
14809 var RecordDef = Roo.data.Record.create([
14810     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14811     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14812 ]);
14813 var myReader = new Roo.data.ArrayReader({
14814     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14815 }, RecordDef);
14816 </code></pre>
14817  * <p>
14818  * This would consume an Array like this:
14819  * <pre><code>
14820 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14821   </code></pre>
14822  
14823  * @constructor
14824  * Create a new JsonReader
14825  * @param {Object} meta Metadata configuration options.
14826  * @param {Object|Array} recordType Either an Array of field definition objects
14827  * 
14828  * @cfg {Array} fields Array of field definition objects
14829  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14830  * as specified to {@link Roo.data.Record#create},
14831  * or an {@link Roo.data.Record} object
14832  *
14833  * 
14834  * created using {@link Roo.data.Record#create}.
14835  */
14836 Roo.data.ArrayReader = function(meta, recordType)
14837 {    
14838     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14839 };
14840
14841 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14842     
14843       /**
14844      * Create a data block containing Roo.data.Records from an XML document.
14845      * @param {Object} o An Array of row objects which represents the dataset.
14846      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14847      * a cache of Roo.data.Records.
14848      */
14849     readRecords : function(o)
14850     {
14851         var sid = this.meta ? this.meta.id : null;
14852         var recordType = this.recordType, fields = recordType.prototype.fields;
14853         var records = [];
14854         var root = o;
14855         for(var i = 0; i < root.length; i++){
14856                 var n = root[i];
14857             var values = {};
14858             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14859             for(var j = 0, jlen = fields.length; j < jlen; j++){
14860                 var f = fields.items[j];
14861                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14862                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14863                 v = f.convert(v);
14864                 values[f.name] = v;
14865             }
14866             var record = new recordType(values, id);
14867             record.json = n;
14868             records[records.length] = record;
14869         }
14870         return {
14871             records : records,
14872             totalRecords : records.length
14873         };
14874     },
14875     // used when loading children.. @see loadDataFromChildren
14876     toLoadData: function(rec)
14877     {
14878         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14879         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14880         
14881     }
14882     
14883     
14884 });/*
14885  * - LGPL
14886  * * 
14887  */
14888
14889 /**
14890  * @class Roo.bootstrap.ComboBox
14891  * @extends Roo.bootstrap.TriggerField
14892  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14893  * @cfg {Boolean} append (true|false) default false
14894  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14895  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14896  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14897  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14898  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14899  * @cfg {Boolean} animate default true
14900  * @cfg {Boolean} emptyResultText only for touch device
14901  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14902  * @cfg {String} emptyTitle default ''
14903  * @cfg {Number} width fixed with? experimental
14904  * @constructor
14905  * Create a new ComboBox.
14906  * @param {Object} config Configuration options
14907  */
14908 Roo.bootstrap.ComboBox = function(config){
14909     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14910     this.addEvents({
14911         /**
14912          * @event expand
14913          * Fires when the dropdown list is expanded
14914         * @param {Roo.bootstrap.ComboBox} combo This combo box
14915         */
14916         'expand' : true,
14917         /**
14918          * @event collapse
14919          * Fires when the dropdown list is collapsed
14920         * @param {Roo.bootstrap.ComboBox} combo This combo box
14921         */
14922         'collapse' : true,
14923         /**
14924          * @event beforeselect
14925          * Fires before a list item is selected. Return false to cancel the selection.
14926         * @param {Roo.bootstrap.ComboBox} combo This combo box
14927         * @param {Roo.data.Record} record The data record returned from the underlying store
14928         * @param {Number} index The index of the selected item in the dropdown list
14929         */
14930         'beforeselect' : true,
14931         /**
14932          * @event select
14933          * Fires when a list item is selected
14934         * @param {Roo.bootstrap.ComboBox} combo This combo box
14935         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14936         * @param {Number} index The index of the selected item in the dropdown list
14937         */
14938         'select' : true,
14939         /**
14940          * @event beforequery
14941          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14942          * The event object passed has these properties:
14943         * @param {Roo.bootstrap.ComboBox} combo This combo box
14944         * @param {String} query The query
14945         * @param {Boolean} forceAll true to force "all" query
14946         * @param {Boolean} cancel true to cancel the query
14947         * @param {Object} e The query event object
14948         */
14949         'beforequery': true,
14950          /**
14951          * @event add
14952          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14953         * @param {Roo.bootstrap.ComboBox} combo This combo box
14954         */
14955         'add' : true,
14956         /**
14957          * @event edit
14958          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14959         * @param {Roo.bootstrap.ComboBox} combo This combo box
14960         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14961         */
14962         'edit' : true,
14963         /**
14964          * @event remove
14965          * Fires when the remove value from the combobox array
14966         * @param {Roo.bootstrap.ComboBox} combo This combo box
14967         */
14968         'remove' : true,
14969         /**
14970          * @event afterremove
14971          * Fires when the remove value from the combobox array
14972         * @param {Roo.bootstrap.ComboBox} combo This combo box
14973         */
14974         'afterremove' : true,
14975         /**
14976          * @event specialfilter
14977          * Fires when specialfilter
14978             * @param {Roo.bootstrap.ComboBox} combo This combo box
14979             */
14980         'specialfilter' : true,
14981         /**
14982          * @event tick
14983          * Fires when tick the element
14984             * @param {Roo.bootstrap.ComboBox} combo This combo box
14985             */
14986         'tick' : true,
14987         /**
14988          * @event touchviewdisplay
14989          * Fires when touch view require special display (default is using displayField)
14990             * @param {Roo.bootstrap.ComboBox} combo This combo box
14991             * @param {Object} cfg set html .
14992             */
14993         'touchviewdisplay' : true
14994         
14995     });
14996     
14997     this.item = [];
14998     this.tickItems = [];
14999     
15000     this.selectedIndex = -1;
15001     if(this.mode == 'local'){
15002         if(config.queryDelay === undefined){
15003             this.queryDelay = 10;
15004         }
15005         if(config.minChars === undefined){
15006             this.minChars = 0;
15007         }
15008     }
15009 };
15010
15011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15012      
15013     /**
15014      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15015      * rendering into an Roo.Editor, defaults to false)
15016      */
15017     /**
15018      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15019      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15020      */
15021     /**
15022      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15023      */
15024     /**
15025      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15026      * the dropdown list (defaults to undefined, with no header element)
15027      */
15028
15029      /**
15030      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15031      */
15032      
15033      /**
15034      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15035      */
15036     listWidth: undefined,
15037     /**
15038      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15039      * mode = 'remote' or 'text' if mode = 'local')
15040      */
15041     displayField: undefined,
15042     
15043     /**
15044      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15045      * mode = 'remote' or 'value' if mode = 'local'). 
15046      * Note: use of a valueField requires the user make a selection
15047      * in order for a value to be mapped.
15048      */
15049     valueField: undefined,
15050     /**
15051      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15052      */
15053     modalTitle : '',
15054     
15055     /**
15056      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15057      * field's data value (defaults to the underlying DOM element's name)
15058      */
15059     hiddenName: undefined,
15060     /**
15061      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15062      */
15063     listClass: '',
15064     /**
15065      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15066      */
15067     selectedClass: 'active',
15068     
15069     /**
15070      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15071      */
15072     shadow:'sides',
15073     /**
15074      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15075      * anchor positions (defaults to 'tl-bl')
15076      */
15077     listAlign: 'tl-bl?',
15078     /**
15079      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15080      */
15081     maxHeight: 300,
15082     /**
15083      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15084      * query specified by the allQuery config option (defaults to 'query')
15085      */
15086     triggerAction: 'query',
15087     /**
15088      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15089      * (defaults to 4, does not apply if editable = false)
15090      */
15091     minChars : 4,
15092     /**
15093      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15094      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15095      */
15096     typeAhead: false,
15097     /**
15098      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15099      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15100      */
15101     queryDelay: 500,
15102     /**
15103      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15104      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15105      */
15106     pageSize: 0,
15107     /**
15108      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15109      * when editable = true (defaults to false)
15110      */
15111     selectOnFocus:false,
15112     /**
15113      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15114      */
15115     queryParam: 'query',
15116     /**
15117      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15118      * when mode = 'remote' (defaults to 'Loading...')
15119      */
15120     loadingText: 'Loading...',
15121     /**
15122      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15123      */
15124     resizable: false,
15125     /**
15126      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15127      */
15128     handleHeight : 8,
15129     /**
15130      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15131      * traditional select (defaults to true)
15132      */
15133     editable: true,
15134     /**
15135      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15136      */
15137     allQuery: '',
15138     /**
15139      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15140      */
15141     mode: 'remote',
15142     /**
15143      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15144      * listWidth has a higher value)
15145      */
15146     minListWidth : 70,
15147     /**
15148      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15149      * allow the user to set arbitrary text into the field (defaults to false)
15150      */
15151     forceSelection:false,
15152     /**
15153      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15154      * if typeAhead = true (defaults to 250)
15155      */
15156     typeAheadDelay : 250,
15157     /**
15158      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15159      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15160      */
15161     valueNotFoundText : undefined,
15162     /**
15163      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15164      */
15165     blockFocus : false,
15166     
15167     /**
15168      * @cfg {Boolean} disableClear Disable showing of clear button.
15169      */
15170     disableClear : false,
15171     /**
15172      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15173      */
15174     alwaysQuery : false,
15175     
15176     /**
15177      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15178      */
15179     multiple : false,
15180     
15181     /**
15182      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15183      */
15184     invalidClass : "has-warning",
15185     
15186     /**
15187      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     validClass : "has-success",
15190     
15191     /**
15192      * @cfg {Boolean} specialFilter (true|false) special filter default false
15193      */
15194     specialFilter : false,
15195     
15196     /**
15197      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15198      */
15199     mobileTouchView : true,
15200     
15201     /**
15202      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15203      */
15204     useNativeIOS : false,
15205     
15206     /**
15207      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15208      */
15209     mobile_restrict_height : false,
15210     
15211     ios_options : false,
15212     
15213     //private
15214     addicon : false,
15215     editicon: false,
15216     
15217     page: 0,
15218     hasQuery: false,
15219     append: false,
15220     loadNext: false,
15221     autoFocus : true,
15222     tickable : false,
15223     btnPosition : 'right',
15224     triggerList : true,
15225     showToggleBtn : true,
15226     animate : true,
15227     emptyResultText: 'Empty',
15228     triggerText : 'Select',
15229     emptyTitle : '',
15230     width : false,
15231     
15232     // element that contains real text value.. (when hidden is used..)
15233     
15234     getAutoCreate : function()
15235     {   
15236         var cfg = false;
15237         //render
15238         /*
15239          * Render classic select for iso
15240          */
15241         
15242         if(Roo.isIOS && this.useNativeIOS){
15243             cfg = this.getAutoCreateNativeIOS();
15244             return cfg;
15245         }
15246         
15247         /*
15248          * Touch Devices
15249          */
15250         
15251         if(Roo.isTouch && this.mobileTouchView){
15252             cfg = this.getAutoCreateTouchView();
15253             return cfg;;
15254         }
15255         
15256         /*
15257          *  Normal ComboBox
15258          */
15259         if(!this.tickable){
15260             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15261             return cfg;
15262         }
15263         
15264         /*
15265          *  ComboBox with tickable selections
15266          */
15267              
15268         var align = this.labelAlign || this.parentLabelAlign();
15269         
15270         cfg = {
15271             cls : 'form-group roo-combobox-tickable' //input-group
15272         };
15273         
15274         var btn_text_select = '';
15275         var btn_text_done = '';
15276         var btn_text_cancel = '';
15277         
15278         if (this.btn_text_show) {
15279             btn_text_select = 'Select';
15280             btn_text_done = 'Done';
15281             btn_text_cancel = 'Cancel'; 
15282         }
15283         
15284         var buttons = {
15285             tag : 'div',
15286             cls : 'tickable-buttons',
15287             cn : [
15288                 {
15289                     tag : 'button',
15290                     type : 'button',
15291                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15292                     //html : this.triggerText
15293                     html: btn_text_select
15294                 },
15295                 {
15296                     tag : 'button',
15297                     type : 'button',
15298                     name : 'ok',
15299                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15300                     //html : 'Done'
15301                     html: btn_text_done
15302                 },
15303                 {
15304                     tag : 'button',
15305                     type : 'button',
15306                     name : 'cancel',
15307                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15308                     //html : 'Cancel'
15309                     html: btn_text_cancel
15310                 }
15311             ]
15312         };
15313         
15314         if(this.editable){
15315             buttons.cn.unshift({
15316                 tag: 'input',
15317                 cls: 'roo-select2-search-field-input'
15318             });
15319         }
15320         
15321         var _this = this;
15322         
15323         Roo.each(buttons.cn, function(c){
15324             if (_this.size) {
15325                 c.cls += ' btn-' + _this.size;
15326             }
15327
15328             if (_this.disabled) {
15329                 c.disabled = true;
15330             }
15331         });
15332         
15333         var box = {
15334             tag: 'div',
15335             style : 'display: contents',
15336             cn: [
15337                 {
15338                     tag: 'input',
15339                     type : 'hidden',
15340                     cls: 'form-hidden-field'
15341                 },
15342                 {
15343                     tag: 'ul',
15344                     cls: 'roo-select2-choices',
15345                     cn:[
15346                         {
15347                             tag: 'li',
15348                             cls: 'roo-select2-search-field',
15349                             cn: [
15350                                 buttons
15351                             ]
15352                         }
15353                     ]
15354                 }
15355             ]
15356         };
15357         
15358         var combobox = {
15359             cls: 'roo-select2-container input-group roo-select2-container-multi',
15360             cn: [
15361                 
15362                 box
15363 //                {
15364 //                    tag: 'ul',
15365 //                    cls: 'typeahead typeahead-long dropdown-menu',
15366 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15367 //                }
15368             ]
15369         };
15370         
15371         if(this.hasFeedback && !this.allowBlank){
15372             
15373             var feedback = {
15374                 tag: 'span',
15375                 cls: 'glyphicon form-control-feedback'
15376             };
15377
15378             combobox.cn.push(feedback);
15379         }
15380         
15381         
15382         
15383         var indicator = {
15384             tag : 'i',
15385             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15386             tooltip : 'This field is required'
15387         };
15388         if (Roo.bootstrap.version == 4) {
15389             indicator = {
15390                 tag : 'i',
15391                 style : 'display:none'
15392             };
15393         }
15394         if (align ==='left' && this.fieldLabel.length) {
15395             
15396             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15397             
15398             cfg.cn = [
15399                 indicator,
15400                 {
15401                     tag: 'label',
15402                     'for' :  id,
15403                     cls : 'control-label col-form-label',
15404                     html : this.fieldLabel
15405
15406                 },
15407                 {
15408                     cls : "", 
15409                     cn: [
15410                         combobox
15411                     ]
15412                 }
15413
15414             ];
15415             
15416             var labelCfg = cfg.cn[1];
15417             var contentCfg = cfg.cn[2];
15418             
15419
15420             if(this.indicatorpos == 'right'){
15421                 
15422                 cfg.cn = [
15423                     {
15424                         tag: 'label',
15425                         'for' :  id,
15426                         cls : 'control-label col-form-label',
15427                         cn : [
15428                             {
15429                                 tag : 'span',
15430                                 html : this.fieldLabel
15431                             },
15432                             indicator
15433                         ]
15434                     },
15435                     {
15436                         cls : "",
15437                         cn: [
15438                             combobox
15439                         ]
15440                     }
15441
15442                 ];
15443                 
15444                 
15445                 
15446                 labelCfg = cfg.cn[0];
15447                 contentCfg = cfg.cn[1];
15448             
15449             }
15450             
15451             if(this.labelWidth > 12){
15452                 labelCfg.style = "width: " + this.labelWidth + 'px';
15453             }
15454             if(this.width * 1 > 0){
15455                 contentCfg.style = "width: " + this.width + 'px';
15456             }
15457             if(this.labelWidth < 13 && this.labelmd == 0){
15458                 this.labelmd = this.labelWidth;
15459             }
15460             
15461             if(this.labellg > 0){
15462                 labelCfg.cls += ' col-lg-' + this.labellg;
15463                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15464             }
15465             
15466             if(this.labelmd > 0){
15467                 labelCfg.cls += ' col-md-' + this.labelmd;
15468                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15469             }
15470             
15471             if(this.labelsm > 0){
15472                 labelCfg.cls += ' col-sm-' + this.labelsm;
15473                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15474             }
15475             
15476             if(this.labelxs > 0){
15477                 labelCfg.cls += ' col-xs-' + this.labelxs;
15478                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15479             }
15480                 
15481                 
15482         } else if ( this.fieldLabel.length) {
15483 //                Roo.log(" label");
15484                  cfg.cn = [
15485                    indicator,
15486                     {
15487                         tag: 'label',
15488                         //cls : 'input-group-addon',
15489                         html : this.fieldLabel
15490                     },
15491                     combobox
15492                 ];
15493                 
15494                 if(this.indicatorpos == 'right'){
15495                     cfg.cn = [
15496                         {
15497                             tag: 'label',
15498                             //cls : 'input-group-addon',
15499                             html : this.fieldLabel
15500                         },
15501                         indicator,
15502                         combobox
15503                     ];
15504                     
15505                 }
15506
15507         } else {
15508             
15509 //                Roo.log(" no label && no align");
15510                 cfg = combobox
15511                      
15512                 
15513         }
15514          
15515         var settings=this;
15516         ['xs','sm','md','lg'].map(function(size){
15517             if (settings[size]) {
15518                 cfg.cls += ' col-' + size + '-' + settings[size];
15519             }
15520         });
15521         
15522         return cfg;
15523         
15524     },
15525     
15526     _initEventsCalled : false,
15527     
15528     // private
15529     initEvents: function()
15530     {   
15531         if (this._initEventsCalled) { // as we call render... prevent looping...
15532             return;
15533         }
15534         this._initEventsCalled = true;
15535         
15536         if (!this.store) {
15537             throw "can not find store for combo";
15538         }
15539         
15540         this.indicator = this.indicatorEl();
15541         
15542         this.store = Roo.factory(this.store, Roo.data);
15543         this.store.parent = this;
15544         
15545         // if we are building from html. then this element is so complex, that we can not really
15546         // use the rendered HTML.
15547         // so we have to trash and replace the previous code.
15548         if (Roo.XComponent.build_from_html) {
15549             // remove this element....
15550             var e = this.el.dom, k=0;
15551             while (e ) { e = e.previousSibling;  ++k;}
15552
15553             this.el.remove();
15554             
15555             this.el=false;
15556             this.rendered = false;
15557             
15558             this.render(this.parent().getChildContainer(true), k);
15559         }
15560         
15561         if(Roo.isIOS && this.useNativeIOS){
15562             this.initIOSView();
15563             return;
15564         }
15565         
15566         /*
15567          * Touch Devices
15568          */
15569         
15570         if(Roo.isTouch && this.mobileTouchView){
15571             this.initTouchView();
15572             return;
15573         }
15574         
15575         if(this.tickable){
15576             this.initTickableEvents();
15577             return;
15578         }
15579         
15580         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15581         
15582         if(this.hiddenName){
15583             
15584             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15585             
15586             this.hiddenField.dom.value =
15587                 this.hiddenValue !== undefined ? this.hiddenValue :
15588                 this.value !== undefined ? this.value : '';
15589
15590             // prevent input submission
15591             this.el.dom.removeAttribute('name');
15592             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15593              
15594              
15595         }
15596         //if(Roo.isGecko){
15597         //    this.el.dom.setAttribute('autocomplete', 'off');
15598         //}
15599         
15600         var cls = 'x-combo-list';
15601         
15602         //this.list = new Roo.Layer({
15603         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15604         //});
15605         
15606         var _this = this;
15607         
15608         (function(){
15609             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15610             _this.list.setWidth(lw);
15611         }).defer(100);
15612         
15613         this.list.on('mouseover', this.onViewOver, this);
15614         this.list.on('mousemove', this.onViewMove, this);
15615         this.list.on('scroll', this.onViewScroll, this);
15616         
15617         /*
15618         this.list.swallowEvent('mousewheel');
15619         this.assetHeight = 0;
15620
15621         if(this.title){
15622             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15623             this.assetHeight += this.header.getHeight();
15624         }
15625
15626         this.innerList = this.list.createChild({cls:cls+'-inner'});
15627         this.innerList.on('mouseover', this.onViewOver, this);
15628         this.innerList.on('mousemove', this.onViewMove, this);
15629         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15630         
15631         if(this.allowBlank && !this.pageSize && !this.disableClear){
15632             this.footer = this.list.createChild({cls:cls+'-ft'});
15633             this.pageTb = new Roo.Toolbar(this.footer);
15634            
15635         }
15636         if(this.pageSize){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15639                     {pageSize: this.pageSize});
15640             
15641         }
15642         
15643         if (this.pageTb && this.allowBlank && !this.disableClear) {
15644             var _this = this;
15645             this.pageTb.add(new Roo.Toolbar.Fill(), {
15646                 cls: 'x-btn-icon x-btn-clear',
15647                 text: '&#160;',
15648                 handler: function()
15649                 {
15650                     _this.collapse();
15651                     _this.clearValue();
15652                     _this.onSelect(false, -1);
15653                 }
15654             });
15655         }
15656         if (this.footer) {
15657             this.assetHeight += this.footer.getHeight();
15658         }
15659         */
15660             
15661         if(!this.tpl){
15662             this.tpl = Roo.bootstrap.version == 4 ?
15663                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15664                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15665         }
15666
15667         this.view = new Roo.View(this.list, this.tpl, {
15668             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15669         });
15670         //this.view.wrapEl.setDisplayed(false);
15671         this.view.on('click', this.onViewClick, this);
15672         
15673         
15674         this.store.on('beforeload', this.onBeforeLoad, this);
15675         this.store.on('load', this.onLoad, this);
15676         this.store.on('loadexception', this.onLoadException, this);
15677         /*
15678         if(this.resizable){
15679             this.resizer = new Roo.Resizable(this.list,  {
15680                pinned:true, handles:'se'
15681             });
15682             this.resizer.on('resize', function(r, w, h){
15683                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15684                 this.listWidth = w;
15685                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15686                 this.restrictHeight();
15687             }, this);
15688             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15689         }
15690         */
15691         if(!this.editable){
15692             this.editable = true;
15693             this.setEditable(false);
15694         }
15695         
15696         /*
15697         
15698         if (typeof(this.events.add.listeners) != 'undefined') {
15699             
15700             this.addicon = this.wrap.createChild(
15701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15702        
15703             this.addicon.on('click', function(e) {
15704                 this.fireEvent('add', this);
15705             }, this);
15706         }
15707         if (typeof(this.events.edit.listeners) != 'undefined') {
15708             
15709             this.editicon = this.wrap.createChild(
15710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15711             if (this.addicon) {
15712                 this.editicon.setStyle('margin-left', '40px');
15713             }
15714             this.editicon.on('click', function(e) {
15715                 
15716                 // we fire even  if inothing is selected..
15717                 this.fireEvent('edit', this, this.lastData );
15718                 
15719             }, this);
15720         }
15721         */
15722         
15723         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15724             "up" : function(e){
15725                 this.inKeyMode = true;
15726                 this.selectPrev();
15727             },
15728
15729             "down" : function(e){
15730                 if(!this.isExpanded()){
15731                     this.onTriggerClick();
15732                 }else{
15733                     this.inKeyMode = true;
15734                     this.selectNext();
15735                 }
15736             },
15737
15738             "enter" : function(e){
15739 //                this.onViewClick();
15740                 //return true;
15741                 this.collapse();
15742                 
15743                 if(this.fireEvent("specialkey", this, e)){
15744                     this.onViewClick(false);
15745                 }
15746                 
15747                 return true;
15748             },
15749
15750             "esc" : function(e){
15751                 this.collapse();
15752             },
15753
15754             "tab" : function(e){
15755                 this.collapse();
15756                 
15757                 if(this.fireEvent("specialkey", this, e)){
15758                     this.onViewClick(false);
15759                 }
15760                 
15761                 return true;
15762             },
15763
15764             scope : this,
15765
15766             doRelay : function(foo, bar, hname){
15767                 if(hname == 'down' || this.scope.isExpanded()){
15768                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15769                 }
15770                 return true;
15771             },
15772
15773             forceKeyDown: true
15774         });
15775         
15776         
15777         this.queryDelay = Math.max(this.queryDelay || 10,
15778                 this.mode == 'local' ? 10 : 250);
15779         
15780         
15781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15782         
15783         if(this.typeAhead){
15784             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15785         }
15786         if(this.editable !== false){
15787             this.inputEl().on("keyup", this.onKeyUp, this);
15788         }
15789         if(this.forceSelection){
15790             this.inputEl().on('blur', this.doForce, this);
15791         }
15792         
15793         if(this.multiple){
15794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15796         }
15797     },
15798     
15799     initTickableEvents: function()
15800     {   
15801         this.createList();
15802         
15803         if(this.hiddenName){
15804             
15805             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15806             
15807             this.hiddenField.dom.value =
15808                 this.hiddenValue !== undefined ? this.hiddenValue :
15809                 this.value !== undefined ? this.value : '';
15810
15811             // prevent input submission
15812             this.el.dom.removeAttribute('name');
15813             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15814              
15815              
15816         }
15817         
15818 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15819         
15820         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15821         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15822         if(this.triggerList){
15823             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15824         }
15825          
15826         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15827         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15828         
15829         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15830         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15831         
15832         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15833         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15834         
15835         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15836         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15837         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15838         
15839         this.okBtn.hide();
15840         this.cancelBtn.hide();
15841         
15842         var _this = this;
15843         
15844         (function(){
15845             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15846             _this.list.setWidth(lw);
15847         }).defer(100);
15848         
15849         this.list.on('mouseover', this.onViewOver, this);
15850         this.list.on('mousemove', this.onViewMove, this);
15851         
15852         this.list.on('scroll', this.onViewScroll, this);
15853         
15854         if(!this.tpl){
15855             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15856                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15857         }
15858
15859         this.view = new Roo.View(this.list, this.tpl, {
15860             singleSelect:true,
15861             tickable:true,
15862             parent:this,
15863             store: this.store,
15864             selectedClass: this.selectedClass
15865         });
15866         
15867         //this.view.wrapEl.setDisplayed(false);
15868         this.view.on('click', this.onViewClick, this);
15869         
15870         
15871         
15872         this.store.on('beforeload', this.onBeforeLoad, this);
15873         this.store.on('load', this.onLoad, this);
15874         this.store.on('loadexception', this.onLoadException, this);
15875         
15876         if(this.editable){
15877             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15878                 "up" : function(e){
15879                     this.inKeyMode = true;
15880                     this.selectPrev();
15881                 },
15882
15883                 "down" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectNext();
15886                 },
15887
15888                 "enter" : function(e){
15889                     if(this.fireEvent("specialkey", this, e)){
15890                         this.onViewClick(false);
15891                     }
15892                     
15893                     return true;
15894                 },
15895
15896                 "esc" : function(e){
15897                     this.onTickableFooterButtonClick(e, false, false);
15898                 },
15899
15900                 "tab" : function(e){
15901                     this.fireEvent("specialkey", this, e);
15902                     
15903                     this.onTickableFooterButtonClick(e, false, false);
15904                     
15905                     return true;
15906                 },
15907
15908                 scope : this,
15909
15910                 doRelay : function(e, fn, key){
15911                     if(this.scope.isExpanded()){
15912                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15913                     }
15914                     return true;
15915                 },
15916
15917                 forceKeyDown: true
15918             });
15919         }
15920         
15921         this.queryDelay = Math.max(this.queryDelay || 10,
15922                 this.mode == 'local' ? 10 : 250);
15923         
15924         
15925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15926         
15927         if(this.typeAhead){
15928             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15929         }
15930         
15931         if(this.editable !== false){
15932             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15933         }
15934         
15935         this.indicator = this.indicatorEl();
15936         
15937         if(this.indicator){
15938             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15939             this.indicator.hide();
15940         }
15941         
15942     },
15943
15944     onDestroy : function(){
15945         if(this.view){
15946             this.view.setStore(null);
15947             this.view.el.removeAllListeners();
15948             this.view.el.remove();
15949             this.view.purgeListeners();
15950         }
15951         if(this.list){
15952             this.list.dom.innerHTML  = '';
15953         }
15954         
15955         if(this.store){
15956             this.store.un('beforeload', this.onBeforeLoad, this);
15957             this.store.un('load', this.onLoad, this);
15958             this.store.un('loadexception', this.onLoadException, this);
15959         }
15960         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15961     },
15962
15963     // private
15964     fireKey : function(e){
15965         if(e.isNavKeyPress() && !this.list.isVisible()){
15966             this.fireEvent("specialkey", this, e);
15967         }
15968     },
15969
15970     // private
15971     onResize: function(w, h)
15972     {
15973         
15974         
15975 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15976 //        
15977 //        if(typeof w != 'number'){
15978 //            // we do not handle it!?!?
15979 //            return;
15980 //        }
15981 //        var tw = this.trigger.getWidth();
15982 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15983 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15984 //        var x = w - tw;
15985 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15986 //            
15987 //        //this.trigger.setStyle('left', x+'px');
15988 //        
15989 //        if(this.list && this.listWidth === undefined){
15990 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15991 //            this.list.setWidth(lw);
15992 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15993 //        }
15994         
15995     
15996         
15997     },
15998
15999     /**
16000      * Allow or prevent the user from directly editing the field text.  If false is passed,
16001      * the user will only be able to select from the items defined in the dropdown list.  This method
16002      * is the runtime equivalent of setting the 'editable' config option at config time.
16003      * @param {Boolean} value True to allow the user to directly edit the field text
16004      */
16005     setEditable : function(value){
16006         if(value == this.editable){
16007             return;
16008         }
16009         this.editable = value;
16010         if(!value){
16011             this.inputEl().dom.setAttribute('readOnly', true);
16012             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16013             this.inputEl().addClass('x-combo-noedit');
16014         }else{
16015             this.inputEl().dom.setAttribute('readOnly', false);
16016             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16017             this.inputEl().removeClass('x-combo-noedit');
16018         }
16019     },
16020
16021     // private
16022     
16023     onBeforeLoad : function(combo,opts){
16024         if(!this.hasFocus){
16025             return;
16026         }
16027          if (!opts.add) {
16028             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16029          }
16030         this.restrictHeight();
16031         this.selectedIndex = -1;
16032     },
16033
16034     // private
16035     onLoad : function(){
16036         
16037         this.hasQuery = false;
16038         
16039         if(!this.hasFocus){
16040             return;
16041         }
16042         
16043         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16044             this.loading.hide();
16045         }
16046         
16047         if(this.store.getCount() > 0){
16048             
16049             this.expand();
16050             this.restrictHeight();
16051             if(this.lastQuery == this.allQuery){
16052                 if(this.editable && !this.tickable){
16053                     this.inputEl().dom.select();
16054                 }
16055                 
16056                 if(
16057                     !this.selectByValue(this.value, true) &&
16058                     this.autoFocus && 
16059                     (
16060                         !this.store.lastOptions ||
16061                         typeof(this.store.lastOptions.add) == 'undefined' || 
16062                         this.store.lastOptions.add != true
16063                     )
16064                 ){
16065                     this.select(0, true);
16066                 }
16067             }else{
16068                 if(this.autoFocus){
16069                     this.selectNext();
16070                 }
16071                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16072                     this.taTask.delay(this.typeAheadDelay);
16073                 }
16074             }
16075         }else{
16076             this.onEmptyResults();
16077         }
16078         
16079         //this.el.focus();
16080     },
16081     // private
16082     onLoadException : function()
16083     {
16084         this.hasQuery = false;
16085         
16086         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16087             this.loading.hide();
16088         }
16089         
16090         if(this.tickable && this.editable){
16091             return;
16092         }
16093         
16094         this.collapse();
16095         // only causes errors at present
16096         //Roo.log(this.store.reader.jsonData);
16097         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16098             // fixme
16099             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16100         //}
16101         
16102         
16103     },
16104     // private
16105     onTypeAhead : function(){
16106         if(this.store.getCount() > 0){
16107             var r = this.store.getAt(0);
16108             var newValue = r.data[this.displayField];
16109             var len = newValue.length;
16110             var selStart = this.getRawValue().length;
16111             
16112             if(selStart != len){
16113                 this.setRawValue(newValue);
16114                 this.selectText(selStart, newValue.length);
16115             }
16116         }
16117     },
16118
16119     // private
16120     onSelect : function(record, index){
16121         
16122         if(this.fireEvent('beforeselect', this, record, index) !== false){
16123         
16124             this.setFromData(index > -1 ? record.data : false);
16125             
16126             this.collapse();
16127             this.fireEvent('select', this, record, index);
16128         }
16129     },
16130
16131     /**
16132      * Returns the currently selected field value or empty string if no value is set.
16133      * @return {String} value The selected value
16134      */
16135     getValue : function()
16136     {
16137         if(Roo.isIOS && this.useNativeIOS){
16138             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16139         }
16140         
16141         if(this.multiple){
16142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16143         }
16144         
16145         if(this.valueField){
16146             return typeof this.value != 'undefined' ? this.value : '';
16147         }else{
16148             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16149         }
16150     },
16151     
16152     getRawValue : function()
16153     {
16154         if(Roo.isIOS && this.useNativeIOS){
16155             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16156         }
16157         
16158         var v = this.inputEl().getValue();
16159         
16160         return v;
16161     },
16162
16163     /**
16164      * Clears any text/value currently set in the field
16165      */
16166     clearValue : function(){
16167         
16168         if(this.hiddenField){
16169             this.hiddenField.dom.value = '';
16170         }
16171         this.value = '';
16172         this.setRawValue('');
16173         this.lastSelectionText = '';
16174         this.lastData = false;
16175         
16176         var close = this.closeTriggerEl();
16177         
16178         if(close){
16179             close.hide();
16180         }
16181         
16182         this.validate();
16183         
16184     },
16185
16186     /**
16187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16188      * will be displayed in the field.  If the value does not match the data value of an existing item,
16189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16190      * Otherwise the field will be blank (although the value will still be set).
16191      * @param {String} value The value to match
16192      */
16193     setValue : function(v)
16194     {
16195         if(Roo.isIOS && this.useNativeIOS){
16196             this.setIOSValue(v);
16197             return;
16198         }
16199         
16200         if(this.multiple){
16201             this.syncValue();
16202             return;
16203         }
16204         
16205         var text = v;
16206         if(this.valueField){
16207             var r = this.findRecord(this.valueField, v);
16208             if(r){
16209                 text = r.data[this.displayField];
16210             }else if(this.valueNotFoundText !== undefined){
16211                 text = this.valueNotFoundText;
16212             }
16213         }
16214         this.lastSelectionText = text;
16215         if(this.hiddenField){
16216             this.hiddenField.dom.value = v;
16217         }
16218         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16219         this.value = v;
16220         
16221         var close = this.closeTriggerEl();
16222         
16223         if(close){
16224             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16225         }
16226         
16227         this.validate();
16228     },
16229     /**
16230      * @property {Object} the last set data for the element
16231      */
16232     
16233     lastData : false,
16234     /**
16235      * Sets the value of the field based on a object which is related to the record format for the store.
16236      * @param {Object} value the value to set as. or false on reset?
16237      */
16238     setFromData : function(o){
16239         
16240         if(this.multiple){
16241             this.addItem(o);
16242             return;
16243         }
16244             
16245         var dv = ''; // display value
16246         var vv = ''; // value value..
16247         this.lastData = o;
16248         if (this.displayField) {
16249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16250         } else {
16251             // this is an error condition!!!
16252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16253         }
16254         
16255         if(this.valueField){
16256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16257         }
16258         
16259         var close = this.closeTriggerEl();
16260         
16261         if(close){
16262             if(dv.length || vv * 1 > 0){
16263                 close.show() ;
16264                 this.blockFocus=true;
16265             } else {
16266                 close.hide();
16267             }             
16268         }
16269         
16270         if(this.hiddenField){
16271             this.hiddenField.dom.value = vv;
16272             
16273             this.lastSelectionText = dv;
16274             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16275             this.value = vv;
16276             return;
16277         }
16278         // no hidden field.. - we store the value in 'value', but still display
16279         // display field!!!!
16280         this.lastSelectionText = dv;
16281         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16282         this.value = vv;
16283         
16284         
16285         
16286     },
16287     // private
16288     reset : function(){
16289         // overridden so that last data is reset..
16290         
16291         if(this.multiple){
16292             this.clearItem();
16293             return;
16294         }
16295         
16296         this.setValue(this.originalValue);
16297         //this.clearInvalid();
16298         this.lastData = false;
16299         if (this.view) {
16300             this.view.clearSelections();
16301         }
16302         
16303         this.validate();
16304     },
16305     // private
16306     findRecord : function(prop, value){
16307         var record;
16308         if(this.store.getCount() > 0){
16309             this.store.each(function(r){
16310                 if(r.data[prop] == value){
16311                     record = r;
16312                     return false;
16313                 }
16314                 return true;
16315             });
16316         }
16317         return record;
16318     },
16319     
16320     getName: function()
16321     {
16322         // returns hidden if it's set..
16323         if (!this.rendered) {return ''};
16324         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16325         
16326     },
16327     // private
16328     onViewMove : function(e, t){
16329         this.inKeyMode = false;
16330     },
16331
16332     // private
16333     onViewOver : function(e, t){
16334         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16335             return;
16336         }
16337         var item = this.view.findItemFromChild(t);
16338         
16339         if(item){
16340             var index = this.view.indexOf(item);
16341             this.select(index, false);
16342         }
16343     },
16344
16345     // private
16346     onViewClick : function(view, doFocus, el, e)
16347     {
16348         var index = this.view.getSelectedIndexes()[0];
16349         
16350         var r = this.store.getAt(index);
16351         
16352         if(this.tickable){
16353             
16354             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16355                 return;
16356             }
16357             
16358             var rm = false;
16359             var _this = this;
16360             
16361             Roo.each(this.tickItems, function(v,k){
16362                 
16363                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16364                     Roo.log(v);
16365                     _this.tickItems.splice(k, 1);
16366                     
16367                     if(typeof(e) == 'undefined' && view == false){
16368                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16369                     }
16370                     
16371                     rm = true;
16372                     return;
16373                 }
16374             });
16375             
16376             if(rm){
16377                 return;
16378             }
16379             
16380             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16381                 this.tickItems.push(r.data);
16382             }
16383             
16384             if(typeof(e) == 'undefined' && view == false){
16385                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16386             }
16387                     
16388             return;
16389         }
16390         
16391         if(r){
16392             this.onSelect(r, index);
16393         }
16394         if(doFocus !== false && !this.blockFocus){
16395             this.inputEl().focus();
16396         }
16397     },
16398
16399     // private
16400     restrictHeight : function(){
16401         //this.innerList.dom.style.height = '';
16402         //var inner = this.innerList.dom;
16403         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16404         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16405         //this.list.beginUpdate();
16406         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16407         this.list.alignTo(this.inputEl(), this.listAlign);
16408         this.list.alignTo(this.inputEl(), this.listAlign);
16409         //this.list.endUpdate();
16410     },
16411
16412     // private
16413     onEmptyResults : function(){
16414         
16415         if(this.tickable && this.editable){
16416             this.hasFocus = false;
16417             this.restrictHeight();
16418             return;
16419         }
16420         
16421         this.collapse();
16422     },
16423
16424     /**
16425      * Returns true if the dropdown list is expanded, else false.
16426      */
16427     isExpanded : function(){
16428         return this.list.isVisible();
16429     },
16430
16431     /**
16432      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16434      * @param {String} value The data value of the item to select
16435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16436      * selected item if it is not currently in view (defaults to true)
16437      * @return {Boolean} True if the value matched an item in the list, else false
16438      */
16439     selectByValue : function(v, scrollIntoView){
16440         if(v !== undefined && v !== null){
16441             var r = this.findRecord(this.valueField || this.displayField, v);
16442             if(r){
16443                 this.select(this.store.indexOf(r), scrollIntoView);
16444                 return true;
16445             }
16446         }
16447         return false;
16448     },
16449
16450     /**
16451      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16453      * @param {Number} index The zero-based index of the list item to select
16454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16455      * selected item if it is not currently in view (defaults to true)
16456      */
16457     select : function(index, scrollIntoView){
16458         this.selectedIndex = index;
16459         this.view.select(index);
16460         if(scrollIntoView !== false){
16461             var el = this.view.getNode(index);
16462             /*
16463              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16464              */
16465             if(el){
16466                 this.list.scrollChildIntoView(el, false);
16467             }
16468         }
16469     },
16470
16471     // private
16472     selectNext : function(){
16473         var ct = this.store.getCount();
16474         if(ct > 0){
16475             if(this.selectedIndex == -1){
16476                 this.select(0);
16477             }else if(this.selectedIndex < ct-1){
16478                 this.select(this.selectedIndex+1);
16479             }
16480         }
16481     },
16482
16483     // private
16484     selectPrev : function(){
16485         var ct = this.store.getCount();
16486         if(ct > 0){
16487             if(this.selectedIndex == -1){
16488                 this.select(0);
16489             }else if(this.selectedIndex != 0){
16490                 this.select(this.selectedIndex-1);
16491             }
16492         }
16493     },
16494
16495     // private
16496     onKeyUp : function(e){
16497         if(this.editable !== false && !e.isSpecialKey()){
16498             this.lastKey = e.getKey();
16499             this.dqTask.delay(this.queryDelay);
16500         }
16501     },
16502
16503     // private
16504     validateBlur : function(){
16505         return !this.list || !this.list.isVisible();   
16506     },
16507
16508     // private
16509     initQuery : function(){
16510         
16511         var v = this.getRawValue();
16512         
16513         if(this.tickable && this.editable){
16514             v = this.tickableInputEl().getValue();
16515         }
16516         
16517         this.doQuery(v);
16518     },
16519
16520     // private
16521     doForce : function(){
16522         if(this.inputEl().dom.value.length > 0){
16523             this.inputEl().dom.value =
16524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16525              
16526         }
16527     },
16528
16529     /**
16530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16531      * query allowing the query action to be canceled if needed.
16532      * @param {String} query The SQL query to execute
16533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16535      * saved in the current store (defaults to false)
16536      */
16537     doQuery : function(q, forceAll){
16538         
16539         if(q === undefined || q === null){
16540             q = '';
16541         }
16542         var qe = {
16543             query: q,
16544             forceAll: forceAll,
16545             combo: this,
16546             cancel:false
16547         };
16548         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16549             return false;
16550         }
16551         q = qe.query;
16552         
16553         forceAll = qe.forceAll;
16554         if(forceAll === true || (q.length >= this.minChars)){
16555             
16556             this.hasQuery = true;
16557             
16558             if(this.lastQuery != q || this.alwaysQuery){
16559                 this.lastQuery = q;
16560                 if(this.mode == 'local'){
16561                     this.selectedIndex = -1;
16562                     if(forceAll){
16563                         this.store.clearFilter();
16564                     }else{
16565                         
16566                         if(this.specialFilter){
16567                             this.fireEvent('specialfilter', this);
16568                             this.onLoad();
16569                             return;
16570                         }
16571                         
16572                         this.store.filter(this.displayField, q);
16573                     }
16574                     
16575                     this.store.fireEvent("datachanged", this.store);
16576                     
16577                     this.onLoad();
16578                     
16579                     
16580                 }else{
16581                     
16582                     this.store.baseParams[this.queryParam] = q;
16583                     
16584                     var options = {params : this.getParams(q)};
16585                     
16586                     if(this.loadNext){
16587                         options.add = true;
16588                         options.params.start = this.page * this.pageSize;
16589                     }
16590                     
16591                     this.store.load(options);
16592                     
16593                     /*
16594                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16595                      *  we should expand the list on onLoad
16596                      *  so command out it
16597                      */
16598 //                    this.expand();
16599                 }
16600             }else{
16601                 this.selectedIndex = -1;
16602                 this.onLoad();   
16603             }
16604         }
16605         
16606         this.loadNext = false;
16607     },
16608     
16609     // private
16610     getParams : function(q){
16611         var p = {};
16612         //p[this.queryParam] = q;
16613         
16614         if(this.pageSize){
16615             p.start = 0;
16616             p.limit = this.pageSize;
16617         }
16618         return p;
16619     },
16620
16621     /**
16622      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16623      */
16624     collapse : function(){
16625         if(!this.isExpanded()){
16626             return;
16627         }
16628         
16629         this.list.hide();
16630         
16631         this.hasFocus = false;
16632         
16633         if(this.tickable){
16634             this.okBtn.hide();
16635             this.cancelBtn.hide();
16636             this.trigger.show();
16637             
16638             if(this.editable){
16639                 this.tickableInputEl().dom.value = '';
16640                 this.tickableInputEl().blur();
16641             }
16642             
16643         }
16644         
16645         Roo.get(document).un('mousedown', this.collapseIf, this);
16646         Roo.get(document).un('mousewheel', this.collapseIf, this);
16647         if (!this.editable) {
16648             Roo.get(document).un('keydown', this.listKeyPress, this);
16649         }
16650         this.fireEvent('collapse', this);
16651         
16652         this.validate();
16653     },
16654
16655     // private
16656     collapseIf : function(e){
16657         var in_combo  = e.within(this.el);
16658         var in_list =  e.within(this.list);
16659         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16660         
16661         if (in_combo || in_list || is_list) {
16662             //e.stopPropagation();
16663             return;
16664         }
16665         
16666         if(this.tickable){
16667             this.onTickableFooterButtonClick(e, false, false);
16668         }
16669
16670         this.collapse();
16671         
16672     },
16673
16674     /**
16675      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16676      */
16677     expand : function(){
16678        
16679         if(this.isExpanded() || !this.hasFocus){
16680             return;
16681         }
16682         
16683         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16684         this.list.setWidth(lw);
16685         
16686         Roo.log('expand');
16687         
16688         this.list.show();
16689         
16690         this.restrictHeight();
16691         
16692         if(this.tickable){
16693             
16694             this.tickItems = Roo.apply([], this.item);
16695             
16696             this.okBtn.show();
16697             this.cancelBtn.show();
16698             this.trigger.hide();
16699             
16700             if(this.editable){
16701                 this.tickableInputEl().focus();
16702             }
16703             
16704         }
16705         
16706         Roo.get(document).on('mousedown', this.collapseIf, this);
16707         Roo.get(document).on('mousewheel', this.collapseIf, this);
16708         if (!this.editable) {
16709             Roo.get(document).on('keydown', this.listKeyPress, this);
16710         }
16711         
16712         this.fireEvent('expand', this);
16713     },
16714
16715     // private
16716     // Implements the default empty TriggerField.onTriggerClick function
16717     onTriggerClick : function(e)
16718     {
16719         Roo.log('trigger click');
16720         
16721         if(this.disabled || !this.triggerList){
16722             return;
16723         }
16724         
16725         this.page = 0;
16726         this.loadNext = false;
16727         
16728         if(this.isExpanded()){
16729             this.collapse();
16730             if (!this.blockFocus) {
16731                 this.inputEl().focus();
16732             }
16733             
16734         }else {
16735             this.hasFocus = true;
16736             if(this.triggerAction == 'all') {
16737                 this.doQuery(this.allQuery, true);
16738             } else {
16739                 this.doQuery(this.getRawValue());
16740             }
16741             if (!this.blockFocus) {
16742                 this.inputEl().focus();
16743             }
16744         }
16745     },
16746     
16747     onTickableTriggerClick : function(e)
16748     {
16749         if(this.disabled){
16750             return;
16751         }
16752         
16753         this.page = 0;
16754         this.loadNext = false;
16755         this.hasFocus = true;
16756         
16757         if(this.triggerAction == 'all') {
16758             this.doQuery(this.allQuery, true);
16759         } else {
16760             this.doQuery(this.getRawValue());
16761         }
16762     },
16763     
16764     onSearchFieldClick : function(e)
16765     {
16766         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16767             this.onTickableFooterButtonClick(e, false, false);
16768             return;
16769         }
16770         
16771         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16772             return;
16773         }
16774         
16775         this.page = 0;
16776         this.loadNext = false;
16777         this.hasFocus = true;
16778         
16779         if(this.triggerAction == 'all') {
16780             this.doQuery(this.allQuery, true);
16781         } else {
16782             this.doQuery(this.getRawValue());
16783         }
16784     },
16785     
16786     listKeyPress : function(e)
16787     {
16788         //Roo.log('listkeypress');
16789         // scroll to first matching element based on key pres..
16790         if (e.isSpecialKey()) {
16791             return false;
16792         }
16793         var k = String.fromCharCode(e.getKey()).toUpperCase();
16794         //Roo.log(k);
16795         var match  = false;
16796         var csel = this.view.getSelectedNodes();
16797         var cselitem = false;
16798         if (csel.length) {
16799             var ix = this.view.indexOf(csel[0]);
16800             cselitem  = this.store.getAt(ix);
16801             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16802                 cselitem = false;
16803             }
16804             
16805         }
16806         
16807         this.store.each(function(v) { 
16808             if (cselitem) {
16809                 // start at existing selection.
16810                 if (cselitem.id == v.id) {
16811                     cselitem = false;
16812                 }
16813                 return true;
16814             }
16815                 
16816             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16817                 match = this.store.indexOf(v);
16818                 return false;
16819             }
16820             return true;
16821         }, this);
16822         
16823         if (match === false) {
16824             return true; // no more action?
16825         }
16826         // scroll to?
16827         this.view.select(match);
16828         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16829         sn.scrollIntoView(sn.dom.parentNode, false);
16830     },
16831     
16832     onViewScroll : function(e, t){
16833         
16834         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){
16835             return;
16836         }
16837         
16838         this.hasQuery = true;
16839         
16840         this.loading = this.list.select('.loading', true).first();
16841         
16842         if(this.loading === null){
16843             this.list.createChild({
16844                 tag: 'div',
16845                 cls: 'loading roo-select2-more-results roo-select2-active',
16846                 html: 'Loading more results...'
16847             });
16848             
16849             this.loading = this.list.select('.loading', true).first();
16850             
16851             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16852             
16853             this.loading.hide();
16854         }
16855         
16856         this.loading.show();
16857         
16858         var _combo = this;
16859         
16860         this.page++;
16861         this.loadNext = true;
16862         
16863         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16864         
16865         return;
16866     },
16867     
16868     addItem : function(o)
16869     {   
16870         var dv = ''; // display value
16871         
16872         if (this.displayField) {
16873             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16874         } else {
16875             // this is an error condition!!!
16876             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16877         }
16878         
16879         if(!dv.length){
16880             return;
16881         }
16882         
16883         var choice = this.choices.createChild({
16884             tag: 'li',
16885             cls: 'roo-select2-search-choice',
16886             cn: [
16887                 {
16888                     tag: 'div',
16889                     html: dv
16890                 },
16891                 {
16892                     tag: 'a',
16893                     href: '#',
16894                     cls: 'roo-select2-search-choice-close fa fa-times',
16895                     tabindex: '-1'
16896                 }
16897             ]
16898             
16899         }, this.searchField);
16900         
16901         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16902         
16903         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16904         
16905         this.item.push(o);
16906         
16907         this.lastData = o;
16908         
16909         this.syncValue();
16910         
16911         this.inputEl().dom.value = '';
16912         
16913         this.validate();
16914     },
16915     
16916     onRemoveItem : function(e, _self, o)
16917     {
16918         e.preventDefault();
16919         
16920         this.lastItem = Roo.apply([], this.item);
16921         
16922         var index = this.item.indexOf(o.data) * 1;
16923         
16924         if( index < 0){
16925             Roo.log('not this item?!');
16926             return;
16927         }
16928         
16929         this.item.splice(index, 1);
16930         o.item.remove();
16931         
16932         this.syncValue();
16933         
16934         this.fireEvent('remove', this, e);
16935         
16936         this.validate();
16937         
16938     },
16939     
16940     syncValue : function()
16941     {
16942         if(!this.item.length){
16943             this.clearValue();
16944             return;
16945         }
16946             
16947         var value = [];
16948         var _this = this;
16949         Roo.each(this.item, function(i){
16950             if(_this.valueField){
16951                 value.push(i[_this.valueField]);
16952                 return;
16953             }
16954
16955             value.push(i);
16956         });
16957
16958         this.value = value.join(',');
16959
16960         if(this.hiddenField){
16961             this.hiddenField.dom.value = this.value;
16962         }
16963         
16964         this.store.fireEvent("datachanged", this.store);
16965         
16966         this.validate();
16967     },
16968     
16969     clearItem : function()
16970     {
16971         if(!this.multiple){
16972             return;
16973         }
16974         
16975         this.item = [];
16976         
16977         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16978            c.remove();
16979         });
16980         
16981         this.syncValue();
16982         
16983         this.validate();
16984         
16985         if(this.tickable && !Roo.isTouch){
16986             this.view.refresh();
16987         }
16988     },
16989     
16990     inputEl: function ()
16991     {
16992         if(Roo.isIOS && this.useNativeIOS){
16993             return this.el.select('select.roo-ios-select', true).first();
16994         }
16995         
16996         if(Roo.isTouch && this.mobileTouchView){
16997             return this.el.select('input.form-control',true).first();
16998         }
16999         
17000         if(this.tickable){
17001             return this.searchField;
17002         }
17003         
17004         return this.el.select('input.form-control',true).first();
17005     },
17006     
17007     onTickableFooterButtonClick : function(e, btn, el)
17008     {
17009         e.preventDefault();
17010         
17011         this.lastItem = Roo.apply([], this.item);
17012         
17013         if(btn && btn.name == 'cancel'){
17014             this.tickItems = Roo.apply([], this.item);
17015             this.collapse();
17016             return;
17017         }
17018         
17019         this.clearItem();
17020         
17021         var _this = this;
17022         
17023         Roo.each(this.tickItems, function(o){
17024             _this.addItem(o);
17025         });
17026         
17027         this.collapse();
17028         
17029     },
17030     
17031     validate : function()
17032     {
17033         if(this.getVisibilityEl().hasClass('hidden')){
17034             return true;
17035         }
17036         
17037         var v = this.getRawValue();
17038         
17039         if(this.multiple){
17040             v = this.getValue();
17041         }
17042         
17043         if(this.disabled || this.allowBlank || v.length){
17044             this.markValid();
17045             return true;
17046         }
17047         
17048         this.markInvalid();
17049         return false;
17050     },
17051     
17052     tickableInputEl : function()
17053     {
17054         if(!this.tickable || !this.editable){
17055             return this.inputEl();
17056         }
17057         
17058         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17059     },
17060     
17061     
17062     getAutoCreateTouchView : function()
17063     {
17064         var id = Roo.id();
17065         
17066         var cfg = {
17067             cls: 'form-group' //input-group
17068         };
17069         
17070         var input =  {
17071             tag: 'input',
17072             id : id,
17073             type : this.inputType,
17074             cls : 'form-control x-combo-noedit',
17075             autocomplete: 'new-password',
17076             placeholder : this.placeholder || '',
17077             readonly : true
17078         };
17079         
17080         if (this.name) {
17081             input.name = this.name;
17082         }
17083         
17084         if (this.size) {
17085             input.cls += ' input-' + this.size;
17086         }
17087         
17088         if (this.disabled) {
17089             input.disabled = true;
17090         }
17091         
17092         var inputblock = {
17093             cls : 'roo-combobox-wrap',
17094             cn : [
17095                 input
17096             ]
17097         };
17098         
17099         if(this.before){
17100             inputblock.cls += ' input-group';
17101             
17102             inputblock.cn.unshift({
17103                 tag :'span',
17104                 cls : 'input-group-addon input-group-prepend input-group-text',
17105                 html : this.before
17106             });
17107         }
17108         
17109         if(this.removable && !this.multiple){
17110             inputblock.cls += ' roo-removable';
17111             
17112             inputblock.cn.push({
17113                 tag: 'button',
17114                 html : 'x',
17115                 cls : 'roo-combo-removable-btn close'
17116             });
17117         }
17118
17119         if(this.hasFeedback && !this.allowBlank){
17120             
17121             inputblock.cls += ' has-feedback';
17122             
17123             inputblock.cn.push({
17124                 tag: 'span',
17125                 cls: 'glyphicon form-control-feedback'
17126             });
17127             
17128         }
17129         
17130         if (this.after) {
17131             
17132             inputblock.cls += (this.before) ? '' : ' input-group';
17133             
17134             inputblock.cn.push({
17135                 tag :'span',
17136                 cls : 'input-group-addon input-group-append input-group-text',
17137                 html : this.after
17138             });
17139         }
17140
17141         
17142         var ibwrap = inputblock;
17143         
17144         if(this.multiple){
17145             ibwrap = {
17146                 tag: 'ul',
17147                 cls: 'roo-select2-choices',
17148                 cn:[
17149                     {
17150                         tag: 'li',
17151                         cls: 'roo-select2-search-field',
17152                         cn: [
17153
17154                             inputblock
17155                         ]
17156                     }
17157                 ]
17158             };
17159         
17160             
17161         }
17162         
17163         var combobox = {
17164             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17165             cn: [
17166                 {
17167                     tag: 'input',
17168                     type : 'hidden',
17169                     cls: 'form-hidden-field'
17170                 },
17171                 ibwrap
17172             ]
17173         };
17174         
17175         if(!this.multiple && this.showToggleBtn){
17176             
17177             var caret = {
17178                 cls: 'caret'
17179             };
17180             
17181             if (this.caret != false) {
17182                 caret = {
17183                      tag: 'i',
17184                      cls: 'fa fa-' + this.caret
17185                 };
17186                 
17187             }
17188             
17189             combobox.cn.push({
17190                 tag :'span',
17191                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17192                 cn : [
17193                     Roo.bootstrap.version == 3 ? caret : '',
17194                     {
17195                         tag: 'span',
17196                         cls: 'combobox-clear',
17197                         cn  : [
17198                             {
17199                                 tag : 'i',
17200                                 cls: 'icon-remove'
17201                             }
17202                         ]
17203                     }
17204                 ]
17205
17206             })
17207         }
17208         
17209         if(this.multiple){
17210             combobox.cls += ' roo-select2-container-multi';
17211         }
17212         
17213         var align = this.labelAlign || this.parentLabelAlign();
17214         
17215         if (align ==='left' && this.fieldLabel.length) {
17216
17217             cfg.cn = [
17218                 {
17219                    tag : 'i',
17220                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17221                    tooltip : 'This field is required'
17222                 },
17223                 {
17224                     tag: 'label',
17225                     cls : 'control-label col-form-label',
17226                     html : this.fieldLabel
17227
17228                 },
17229                 {
17230                     cls : 'roo-combobox-wrap ', 
17231                     cn: [
17232                         combobox
17233                     ]
17234                 }
17235             ];
17236             
17237             var labelCfg = cfg.cn[1];
17238             var contentCfg = cfg.cn[2];
17239             
17240
17241             if(this.indicatorpos == 'right'){
17242                 cfg.cn = [
17243                     {
17244                         tag: 'label',
17245                         'for' :  id,
17246                         cls : 'control-label col-form-label',
17247                         cn : [
17248                             {
17249                                 tag : 'span',
17250                                 html : this.fieldLabel
17251                             },
17252                             {
17253                                 tag : 'i',
17254                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17255                                 tooltip : 'This field is required'
17256                             }
17257                         ]
17258                     },
17259                     {
17260                         cls : "roo-combobox-wrap ",
17261                         cn: [
17262                             combobox
17263                         ]
17264                     }
17265
17266                 ];
17267                 
17268                 labelCfg = cfg.cn[0];
17269                 contentCfg = cfg.cn[1];
17270             }
17271             
17272            
17273             
17274             if(this.labelWidth > 12){
17275                 labelCfg.style = "width: " + this.labelWidth + 'px';
17276             }
17277            
17278             if(this.labelWidth < 13 && this.labelmd == 0){
17279                 this.labelmd = this.labelWidth;
17280             }
17281             
17282             if(this.labellg > 0){
17283                 labelCfg.cls += ' col-lg-' + this.labellg;
17284                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17285             }
17286             
17287             if(this.labelmd > 0){
17288                 labelCfg.cls += ' col-md-' + this.labelmd;
17289                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17290             }
17291             
17292             if(this.labelsm > 0){
17293                 labelCfg.cls += ' col-sm-' + this.labelsm;
17294                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17295             }
17296             
17297             if(this.labelxs > 0){
17298                 labelCfg.cls += ' col-xs-' + this.labelxs;
17299                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17300             }
17301                 
17302                 
17303         } else if ( this.fieldLabel.length) {
17304             cfg.cn = [
17305                 {
17306                    tag : 'i',
17307                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17308                    tooltip : 'This field is required'
17309                 },
17310                 {
17311                     tag: 'label',
17312                     cls : 'control-label',
17313                     html : this.fieldLabel
17314
17315                 },
17316                 {
17317                     cls : '', 
17318                     cn: [
17319                         combobox
17320                     ]
17321                 }
17322             ];
17323             
17324             if(this.indicatorpos == 'right'){
17325                 cfg.cn = [
17326                     {
17327                         tag: 'label',
17328                         cls : 'control-label',
17329                         html : this.fieldLabel,
17330                         cn : [
17331                             {
17332                                tag : 'i',
17333                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17334                                tooltip : 'This field is required'
17335                             }
17336                         ]
17337                     },
17338                     {
17339                         cls : '', 
17340                         cn: [
17341                             combobox
17342                         ]
17343                     }
17344                 ];
17345             }
17346         } else {
17347             cfg.cn = combobox;    
17348         }
17349         
17350         
17351         var settings = this;
17352         
17353         ['xs','sm','md','lg'].map(function(size){
17354             if (settings[size]) {
17355                 cfg.cls += ' col-' + size + '-' + settings[size];
17356             }
17357         });
17358         
17359         return cfg;
17360     },
17361     
17362     initTouchView : function()
17363     {
17364         this.renderTouchView();
17365         
17366         this.touchViewEl.on('scroll', function(){
17367             this.el.dom.scrollTop = 0;
17368         }, this);
17369         
17370         this.originalValue = this.getValue();
17371         
17372         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17373         
17374         this.inputEl().on("click", this.showTouchView, this);
17375         if (this.triggerEl) {
17376             this.triggerEl.on("click", this.showTouchView, this);
17377         }
17378         
17379         
17380         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17381         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17382         
17383         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17384         
17385         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17386         this.store.on('load', this.onTouchViewLoad, this);
17387         this.store.on('loadexception', this.onTouchViewLoadException, this);
17388         
17389         if(this.hiddenName){
17390             
17391             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17392             
17393             this.hiddenField.dom.value =
17394                 this.hiddenValue !== undefined ? this.hiddenValue :
17395                 this.value !== undefined ? this.value : '';
17396         
17397             this.el.dom.removeAttribute('name');
17398             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17399         }
17400         
17401         if(this.multiple){
17402             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17403             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17404         }
17405         
17406         if(this.removable && !this.multiple){
17407             var close = this.closeTriggerEl();
17408             if(close){
17409                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17410                 close.on('click', this.removeBtnClick, this, close);
17411             }
17412         }
17413         /*
17414          * fix the bug in Safari iOS8
17415          */
17416         this.inputEl().on("focus", function(e){
17417             document.activeElement.blur();
17418         }, this);
17419         
17420         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17421         
17422         return;
17423         
17424         
17425     },
17426     
17427     renderTouchView : function()
17428     {
17429         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17430         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17431         
17432         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17433         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434         
17435         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17436         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437         this.touchViewBodyEl.setStyle('overflow', 'auto');
17438         
17439         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17440         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17441         
17442         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17443         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444         
17445     },
17446     
17447     showTouchView : function()
17448     {
17449         if(this.disabled){
17450             return;
17451         }
17452         
17453         this.touchViewHeaderEl.hide();
17454
17455         if(this.modalTitle.length){
17456             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17457             this.touchViewHeaderEl.show();
17458         }
17459
17460         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17461         this.touchViewEl.show();
17462
17463         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17464         
17465         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17466         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17467
17468         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17469
17470         if(this.modalTitle.length){
17471             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17472         }
17473         
17474         this.touchViewBodyEl.setHeight(bodyHeight);
17475
17476         if(this.animate){
17477             var _this = this;
17478             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17479         }else{
17480             this.touchViewEl.addClass(['in','show']);
17481         }
17482         
17483         if(this._touchViewMask){
17484             Roo.get(document.body).addClass("x-body-masked");
17485             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17486             this._touchViewMask.setStyle('z-index', 10000);
17487             this._touchViewMask.addClass('show');
17488         }
17489         
17490         this.doTouchViewQuery();
17491         
17492     },
17493     
17494     hideTouchView : function()
17495     {
17496         this.touchViewEl.removeClass(['in','show']);
17497
17498         if(this.animate){
17499             var _this = this;
17500             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17501         }else{
17502             this.touchViewEl.setStyle('display', 'none');
17503         }
17504         
17505         if(this._touchViewMask){
17506             this._touchViewMask.removeClass('show');
17507             Roo.get(document.body).removeClass("x-body-masked");
17508         }
17509     },
17510     
17511     setTouchViewValue : function()
17512     {
17513         if(this.multiple){
17514             this.clearItem();
17515         
17516             var _this = this;
17517
17518             Roo.each(this.tickItems, function(o){
17519                 this.addItem(o);
17520             }, this);
17521         }
17522         
17523         this.hideTouchView();
17524     },
17525     
17526     doTouchViewQuery : function()
17527     {
17528         var qe = {
17529             query: '',
17530             forceAll: true,
17531             combo: this,
17532             cancel:false
17533         };
17534         
17535         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17536             return false;
17537         }
17538         
17539         if(!this.alwaysQuery || this.mode == 'local'){
17540             this.onTouchViewLoad();
17541             return;
17542         }
17543         
17544         this.store.load();
17545     },
17546     
17547     onTouchViewBeforeLoad : function(combo,opts)
17548     {
17549         return;
17550     },
17551
17552     // private
17553     onTouchViewLoad : function()
17554     {
17555         if(this.store.getCount() < 1){
17556             this.onTouchViewEmptyResults();
17557             return;
17558         }
17559         
17560         this.clearTouchView();
17561         
17562         var rawValue = this.getRawValue();
17563         
17564         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17565         
17566         this.tickItems = [];
17567         
17568         this.store.data.each(function(d, rowIndex){
17569             var row = this.touchViewListGroup.createChild(template);
17570             
17571             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17572                 row.addClass(d.data.cls);
17573             }
17574             
17575             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17576                 var cfg = {
17577                     data : d.data,
17578                     html : d.data[this.displayField]
17579                 };
17580                 
17581                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17582                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17583                 }
17584             }
17585             row.removeClass('selected');
17586             if(!this.multiple && this.valueField &&
17587                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17588             {
17589                 // radio buttons..
17590                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17591                 row.addClass('selected');
17592             }
17593             
17594             if(this.multiple && this.valueField &&
17595                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17596             {
17597                 
17598                 // checkboxes...
17599                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17600                 this.tickItems.push(d.data);
17601             }
17602             
17603             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17604             
17605         }, this);
17606         
17607         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17608         
17609         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17610
17611         if(this.modalTitle.length){
17612             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17613         }
17614
17615         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17616         
17617         if(this.mobile_restrict_height && listHeight < bodyHeight){
17618             this.touchViewBodyEl.setHeight(listHeight);
17619         }
17620         
17621         var _this = this;
17622         
17623         if(firstChecked && listHeight > bodyHeight){
17624             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17625         }
17626         
17627     },
17628     
17629     onTouchViewLoadException : function()
17630     {
17631         this.hideTouchView();
17632     },
17633     
17634     onTouchViewEmptyResults : function()
17635     {
17636         this.clearTouchView();
17637         
17638         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17639         
17640         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17641         
17642     },
17643     
17644     clearTouchView : function()
17645     {
17646         this.touchViewListGroup.dom.innerHTML = '';
17647     },
17648     
17649     onTouchViewClick : function(e, el, o)
17650     {
17651         e.preventDefault();
17652         
17653         var row = o.row;
17654         var rowIndex = o.rowIndex;
17655         
17656         var r = this.store.getAt(rowIndex);
17657         
17658         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17659             
17660             if(!this.multiple){
17661                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17662                     c.dom.removeAttribute('checked');
17663                 }, this);
17664
17665                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17666
17667                 this.setFromData(r.data);
17668
17669                 var close = this.closeTriggerEl();
17670
17671                 if(close){
17672                     close.show();
17673                 }
17674
17675                 this.hideTouchView();
17676
17677                 this.fireEvent('select', this, r, rowIndex);
17678
17679                 return;
17680             }
17681
17682             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17683                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17684                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17685                 return;
17686             }
17687
17688             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689             this.addItem(r.data);
17690             this.tickItems.push(r.data);
17691         }
17692     },
17693     
17694     getAutoCreateNativeIOS : function()
17695     {
17696         var cfg = {
17697             cls: 'form-group' //input-group,
17698         };
17699         
17700         var combobox =  {
17701             tag: 'select',
17702             cls : 'roo-ios-select'
17703         };
17704         
17705         if (this.name) {
17706             combobox.name = this.name;
17707         }
17708         
17709         if (this.disabled) {
17710             combobox.disabled = true;
17711         }
17712         
17713         var settings = this;
17714         
17715         ['xs','sm','md','lg'].map(function(size){
17716             if (settings[size]) {
17717                 cfg.cls += ' col-' + size + '-' + settings[size];
17718             }
17719         });
17720         
17721         cfg.cn = combobox;
17722         
17723         return cfg;
17724         
17725     },
17726     
17727     initIOSView : function()
17728     {
17729         this.store.on('load', this.onIOSViewLoad, this);
17730         
17731         return;
17732     },
17733     
17734     onIOSViewLoad : function()
17735     {
17736         if(this.store.getCount() < 1){
17737             return;
17738         }
17739         
17740         this.clearIOSView();
17741         
17742         if(this.allowBlank) {
17743             
17744             var default_text = '-- SELECT --';
17745             
17746             if(this.placeholder.length){
17747                 default_text = this.placeholder;
17748             }
17749             
17750             if(this.emptyTitle.length){
17751                 default_text += ' - ' + this.emptyTitle + ' -';
17752             }
17753             
17754             var opt = this.inputEl().createChild({
17755                 tag: 'option',
17756                 value : 0,
17757                 html : default_text
17758             });
17759             
17760             var o = {};
17761             o[this.valueField] = 0;
17762             o[this.displayField] = default_text;
17763             
17764             this.ios_options.push({
17765                 data : o,
17766                 el : opt
17767             });
17768             
17769         }
17770         
17771         this.store.data.each(function(d, rowIndex){
17772             
17773             var html = '';
17774             
17775             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17776                 html = d.data[this.displayField];
17777             }
17778             
17779             var value = '';
17780             
17781             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17782                 value = d.data[this.valueField];
17783             }
17784             
17785             var option = {
17786                 tag: 'option',
17787                 value : value,
17788                 html : html
17789             };
17790             
17791             if(this.value == d.data[this.valueField]){
17792                 option['selected'] = true;
17793             }
17794             
17795             var opt = this.inputEl().createChild(option);
17796             
17797             this.ios_options.push({
17798                 data : d.data,
17799                 el : opt
17800             });
17801             
17802         }, this);
17803         
17804         this.inputEl().on('change', function(){
17805            this.fireEvent('select', this);
17806         }, this);
17807         
17808     },
17809     
17810     clearIOSView: function()
17811     {
17812         this.inputEl().dom.innerHTML = '';
17813         
17814         this.ios_options = [];
17815     },
17816     
17817     setIOSValue: function(v)
17818     {
17819         this.value = v;
17820         
17821         if(!this.ios_options){
17822             return;
17823         }
17824         
17825         Roo.each(this.ios_options, function(opts){
17826            
17827            opts.el.dom.removeAttribute('selected');
17828            
17829            if(opts.data[this.valueField] != v){
17830                return;
17831            }
17832            
17833            opts.el.dom.setAttribute('selected', true);
17834            
17835         }, this);
17836     }
17837
17838     /** 
17839     * @cfg {Boolean} grow 
17840     * @hide 
17841     */
17842     /** 
17843     * @cfg {Number} growMin 
17844     * @hide 
17845     */
17846     /** 
17847     * @cfg {Number} growMax 
17848     * @hide 
17849     */
17850     /**
17851      * @hide
17852      * @method autoSize
17853      */
17854 });
17855
17856 Roo.apply(Roo.bootstrap.ComboBox,  {
17857     
17858     header : {
17859         tag: 'div',
17860         cls: 'modal-header',
17861         cn: [
17862             {
17863                 tag: 'h4',
17864                 cls: 'modal-title'
17865             }
17866         ]
17867     },
17868     
17869     body : {
17870         tag: 'div',
17871         cls: 'modal-body',
17872         cn: [
17873             {
17874                 tag: 'ul',
17875                 cls: 'list-group'
17876             }
17877         ]
17878     },
17879     
17880     listItemRadio : {
17881         tag: 'li',
17882         cls: 'list-group-item',
17883         cn: [
17884             {
17885                 tag: 'span',
17886                 cls: 'roo-combobox-list-group-item-value'
17887             },
17888             {
17889                 tag: 'div',
17890                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17891                 cn: [
17892                     {
17893                         tag: 'input',
17894                         type: 'radio'
17895                     },
17896                     {
17897                         tag: 'label'
17898                     }
17899                 ]
17900             }
17901         ]
17902     },
17903     
17904     listItemCheckbox : {
17905         tag: 'li',
17906         cls: 'list-group-item',
17907         cn: [
17908             {
17909                 tag: 'span',
17910                 cls: 'roo-combobox-list-group-item-value'
17911             },
17912             {
17913                 tag: 'div',
17914                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17915                 cn: [
17916                     {
17917                         tag: 'input',
17918                         type: 'checkbox'
17919                     },
17920                     {
17921                         tag: 'label'
17922                     }
17923                 ]
17924             }
17925         ]
17926     },
17927     
17928     emptyResult : {
17929         tag: 'div',
17930         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17931     },
17932     
17933     footer : {
17934         tag: 'div',
17935         cls: 'modal-footer',
17936         cn: [
17937             {
17938                 tag: 'div',
17939                 cls: 'row',
17940                 cn: [
17941                     {
17942                         tag: 'div',
17943                         cls: 'col-xs-6 text-left',
17944                         cn: {
17945                             tag: 'button',
17946                             cls: 'btn btn-danger roo-touch-view-cancel',
17947                             html: 'Cancel'
17948                         }
17949                     },
17950                     {
17951                         tag: 'div',
17952                         cls: 'col-xs-6 text-right',
17953                         cn: {
17954                             tag: 'button',
17955                             cls: 'btn btn-success roo-touch-view-ok',
17956                             html: 'OK'
17957                         }
17958                     }
17959                 ]
17960             }
17961         ]
17962         
17963     }
17964 });
17965
17966 Roo.apply(Roo.bootstrap.ComboBox,  {
17967     
17968     touchViewTemplate : {
17969         tag: 'div',
17970         cls: 'modal fade roo-combobox-touch-view',
17971         cn: [
17972             {
17973                 tag: 'div',
17974                 cls: 'modal-dialog',
17975                 style : 'position:fixed', // we have to fix position....
17976                 cn: [
17977                     {
17978                         tag: 'div',
17979                         cls: 'modal-content',
17980                         cn: [
17981                             Roo.bootstrap.ComboBox.header,
17982                             Roo.bootstrap.ComboBox.body,
17983                             Roo.bootstrap.ComboBox.footer
17984                         ]
17985                     }
17986                 ]
17987             }
17988         ]
17989     }
17990 });/*
17991  * Based on:
17992  * Ext JS Library 1.1.1
17993  * Copyright(c) 2006-2007, Ext JS, LLC.
17994  *
17995  * Originally Released Under LGPL - original licence link has changed is not relivant.
17996  *
17997  * Fork - LGPL
17998  * <script type="text/javascript">
17999  */
18000
18001 /**
18002  * @class Roo.View
18003  * @extends Roo.util.Observable
18004  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18005  * This class also supports single and multi selection modes. <br>
18006  * Create a data model bound view:
18007  <pre><code>
18008  var store = new Roo.data.Store(...);
18009
18010  var view = new Roo.View({
18011     el : "my-element",
18012     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18013  
18014     singleSelect: true,
18015     selectedClass: "ydataview-selected",
18016     store: store
18017  });
18018
18019  // listen for node click?
18020  view.on("click", function(vw, index, node, e){
18021  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18022  });
18023
18024  // load XML data
18025  dataModel.load("foobar.xml");
18026  </code></pre>
18027  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18028  * <br><br>
18029  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18030  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18031  * 
18032  * Note: old style constructor is still suported (container, template, config)
18033  * 
18034  * @constructor
18035  * Create a new View
18036  * @param {Object} config The config object
18037  * 
18038  */
18039 Roo.View = function(config, depreciated_tpl, depreciated_config){
18040     
18041     this.parent = false;
18042     
18043     if (typeof(depreciated_tpl) == 'undefined') {
18044         // new way.. - universal constructor.
18045         Roo.apply(this, config);
18046         this.el  = Roo.get(this.el);
18047     } else {
18048         // old format..
18049         this.el  = Roo.get(config);
18050         this.tpl = depreciated_tpl;
18051         Roo.apply(this, depreciated_config);
18052     }
18053     this.wrapEl  = this.el.wrap().wrap();
18054     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18055     
18056     
18057     if(typeof(this.tpl) == "string"){
18058         this.tpl = new Roo.Template(this.tpl);
18059     } else {
18060         // support xtype ctors..
18061         this.tpl = new Roo.factory(this.tpl, Roo);
18062     }
18063     
18064     
18065     this.tpl.compile();
18066     
18067     /** @private */
18068     this.addEvents({
18069         /**
18070          * @event beforeclick
18071          * Fires before a click is processed. Returns false to cancel the default action.
18072          * @param {Roo.View} this
18073          * @param {Number} index The index of the target node
18074          * @param {HTMLElement} node The target node
18075          * @param {Roo.EventObject} e The raw event object
18076          */
18077             "beforeclick" : true,
18078         /**
18079          * @event click
18080          * Fires when a template node is clicked.
18081          * @param {Roo.View} this
18082          * @param {Number} index The index of the target node
18083          * @param {HTMLElement} node The target node
18084          * @param {Roo.EventObject} e The raw event object
18085          */
18086             "click" : true,
18087         /**
18088          * @event dblclick
18089          * Fires when a template node is double clicked.
18090          * @param {Roo.View} this
18091          * @param {Number} index The index of the target node
18092          * @param {HTMLElement} node The target node
18093          * @param {Roo.EventObject} e The raw event object
18094          */
18095             "dblclick" : true,
18096         /**
18097          * @event contextmenu
18098          * Fires when a template node is right clicked.
18099          * @param {Roo.View} this
18100          * @param {Number} index The index of the target node
18101          * @param {HTMLElement} node The target node
18102          * @param {Roo.EventObject} e The raw event object
18103          */
18104             "contextmenu" : true,
18105         /**
18106          * @event selectionchange
18107          * Fires when the selected nodes change.
18108          * @param {Roo.View} this
18109          * @param {Array} selections Array of the selected nodes
18110          */
18111             "selectionchange" : true,
18112     
18113         /**
18114          * @event beforeselect
18115          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18116          * @param {Roo.View} this
18117          * @param {HTMLElement} node The node to be selected
18118          * @param {Array} selections Array of currently selected nodes
18119          */
18120             "beforeselect" : true,
18121         /**
18122          * @event preparedata
18123          * Fires on every row to render, to allow you to change the data.
18124          * @param {Roo.View} this
18125          * @param {Object} data to be rendered (change this)
18126          */
18127           "preparedata" : true
18128           
18129           
18130         });
18131
18132
18133
18134     this.el.on({
18135         "click": this.onClick,
18136         "dblclick": this.onDblClick,
18137         "contextmenu": this.onContextMenu,
18138         scope:this
18139     });
18140
18141     this.selections = [];
18142     this.nodes = [];
18143     this.cmp = new Roo.CompositeElementLite([]);
18144     if(this.store){
18145         this.store = Roo.factory(this.store, Roo.data);
18146         this.setStore(this.store, true);
18147     }
18148     
18149     if ( this.footer && this.footer.xtype) {
18150            
18151          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18152         
18153         this.footer.dataSource = this.store;
18154         this.footer.container = fctr;
18155         this.footer = Roo.factory(this.footer, Roo);
18156         fctr.insertFirst(this.el);
18157         
18158         // this is a bit insane - as the paging toolbar seems to detach the el..
18159 //        dom.parentNode.parentNode.parentNode
18160          // they get detached?
18161     }
18162     
18163     
18164     Roo.View.superclass.constructor.call(this);
18165     
18166     
18167 };
18168
18169 Roo.extend(Roo.View, Roo.util.Observable, {
18170     
18171      /**
18172      * @cfg {Roo.data.Store} store Data store to load data from.
18173      */
18174     store : false,
18175     
18176     /**
18177      * @cfg {String|Roo.Element} el The container element.
18178      */
18179     el : '',
18180     
18181     /**
18182      * @cfg {String|Roo.Template} tpl The template used by this View 
18183      */
18184     tpl : false,
18185     /**
18186      * @cfg {String} dataName the named area of the template to use as the data area
18187      *                          Works with domtemplates roo-name="name"
18188      */
18189     dataName: false,
18190     /**
18191      * @cfg {String} selectedClass The css class to add to selected nodes
18192      */
18193     selectedClass : "x-view-selected",
18194      /**
18195      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18196      */
18197     emptyText : "",
18198     
18199     /**
18200      * @cfg {String} text to display on mask (default Loading)
18201      */
18202     mask : false,
18203     /**
18204      * @cfg {Boolean} multiSelect Allow multiple selection
18205      */
18206     multiSelect : false,
18207     /**
18208      * @cfg {Boolean} singleSelect Allow single selection
18209      */
18210     singleSelect:  false,
18211     
18212     /**
18213      * @cfg {Boolean} toggleSelect - selecting 
18214      */
18215     toggleSelect : false,
18216     
18217     /**
18218      * @cfg {Boolean} tickable - selecting 
18219      */
18220     tickable : false,
18221     
18222     /**
18223      * Returns the element this view is bound to.
18224      * @return {Roo.Element}
18225      */
18226     getEl : function(){
18227         return this.wrapEl;
18228     },
18229     
18230     
18231
18232     /**
18233      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18234      */
18235     refresh : function(){
18236         //Roo.log('refresh');
18237         var t = this.tpl;
18238         
18239         // if we are using something like 'domtemplate', then
18240         // the what gets used is:
18241         // t.applySubtemplate(NAME, data, wrapping data..)
18242         // the outer template then get' applied with
18243         //     the store 'extra data'
18244         // and the body get's added to the
18245         //      roo-name="data" node?
18246         //      <span class='roo-tpl-{name}'></span> ?????
18247         
18248         
18249         
18250         this.clearSelections();
18251         this.el.update("");
18252         var html = [];
18253         var records = this.store.getRange();
18254         if(records.length < 1) {
18255             
18256             // is this valid??  = should it render a template??
18257             
18258             this.el.update(this.emptyText);
18259             return;
18260         }
18261         var el = this.el;
18262         if (this.dataName) {
18263             this.el.update(t.apply(this.store.meta)); //????
18264             el = this.el.child('.roo-tpl-' + this.dataName);
18265         }
18266         
18267         for(var i = 0, len = records.length; i < len; i++){
18268             var data = this.prepareData(records[i].data, i, records[i]);
18269             this.fireEvent("preparedata", this, data, i, records[i]);
18270             
18271             var d = Roo.apply({}, data);
18272             
18273             if(this.tickable){
18274                 Roo.apply(d, {'roo-id' : Roo.id()});
18275                 
18276                 var _this = this;
18277             
18278                 Roo.each(this.parent.item, function(item){
18279                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18280                         return;
18281                     }
18282                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18283                 });
18284             }
18285             
18286             html[html.length] = Roo.util.Format.trim(
18287                 this.dataName ?
18288                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18289                     t.apply(d)
18290             );
18291         }
18292         
18293         
18294         
18295         el.update(html.join(""));
18296         this.nodes = el.dom.childNodes;
18297         this.updateIndexes(0);
18298     },
18299     
18300
18301     /**
18302      * Function to override to reformat the data that is sent to
18303      * the template for each node.
18304      * DEPRICATED - use the preparedata event handler.
18305      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18306      * a JSON object for an UpdateManager bound view).
18307      */
18308     prepareData : function(data, index, record)
18309     {
18310         this.fireEvent("preparedata", this, data, index, record);
18311         return data;
18312     },
18313
18314     onUpdate : function(ds, record){
18315         // Roo.log('on update');   
18316         this.clearSelections();
18317         var index = this.store.indexOf(record);
18318         var n = this.nodes[index];
18319         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18320         n.parentNode.removeChild(n);
18321         this.updateIndexes(index, index);
18322     },
18323
18324     
18325     
18326 // --------- FIXME     
18327     onAdd : function(ds, records, index)
18328     {
18329         //Roo.log(['on Add', ds, records, index] );        
18330         this.clearSelections();
18331         if(this.nodes.length == 0){
18332             this.refresh();
18333             return;
18334         }
18335         var n = this.nodes[index];
18336         for(var i = 0, len = records.length; i < len; i++){
18337             var d = this.prepareData(records[i].data, i, records[i]);
18338             if(n){
18339                 this.tpl.insertBefore(n, d);
18340             }else{
18341                 
18342                 this.tpl.append(this.el, d);
18343             }
18344         }
18345         this.updateIndexes(index);
18346     },
18347
18348     onRemove : function(ds, record, index){
18349        // Roo.log('onRemove');
18350         this.clearSelections();
18351         var el = this.dataName  ?
18352             this.el.child('.roo-tpl-' + this.dataName) :
18353             this.el; 
18354         
18355         el.dom.removeChild(this.nodes[index]);
18356         this.updateIndexes(index);
18357     },
18358
18359     /**
18360      * Refresh an individual node.
18361      * @param {Number} index
18362      */
18363     refreshNode : function(index){
18364         this.onUpdate(this.store, this.store.getAt(index));
18365     },
18366
18367     updateIndexes : function(startIndex, endIndex){
18368         var ns = this.nodes;
18369         startIndex = startIndex || 0;
18370         endIndex = endIndex || ns.length - 1;
18371         for(var i = startIndex; i <= endIndex; i++){
18372             ns[i].nodeIndex = i;
18373         }
18374     },
18375
18376     /**
18377      * Changes the data store this view uses and refresh the view.
18378      * @param {Store} store
18379      */
18380     setStore : function(store, initial){
18381         if(!initial && this.store){
18382             this.store.un("datachanged", this.refresh);
18383             this.store.un("add", this.onAdd);
18384             this.store.un("remove", this.onRemove);
18385             this.store.un("update", this.onUpdate);
18386             this.store.un("clear", this.refresh);
18387             this.store.un("beforeload", this.onBeforeLoad);
18388             this.store.un("load", this.onLoad);
18389             this.store.un("loadexception", this.onLoad);
18390         }
18391         if(store){
18392           
18393             store.on("datachanged", this.refresh, this);
18394             store.on("add", this.onAdd, this);
18395             store.on("remove", this.onRemove, this);
18396             store.on("update", this.onUpdate, this);
18397             store.on("clear", this.refresh, this);
18398             store.on("beforeload", this.onBeforeLoad, this);
18399             store.on("load", this.onLoad, this);
18400             store.on("loadexception", this.onLoad, this);
18401         }
18402         
18403         if(store){
18404             this.refresh();
18405         }
18406     },
18407     /**
18408      * onbeforeLoad - masks the loading area.
18409      *
18410      */
18411     onBeforeLoad : function(store,opts)
18412     {
18413          //Roo.log('onBeforeLoad');   
18414         if (!opts.add) {
18415             this.el.update("");
18416         }
18417         this.el.mask(this.mask ? this.mask : "Loading" ); 
18418     },
18419     onLoad : function ()
18420     {
18421         this.el.unmask();
18422     },
18423     
18424
18425     /**
18426      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18427      * @param {HTMLElement} node
18428      * @return {HTMLElement} The template node
18429      */
18430     findItemFromChild : function(node){
18431         var el = this.dataName  ?
18432             this.el.child('.roo-tpl-' + this.dataName,true) :
18433             this.el.dom; 
18434         
18435         if(!node || node.parentNode == el){
18436                     return node;
18437             }
18438             var p = node.parentNode;
18439             while(p && p != el){
18440             if(p.parentNode == el){
18441                 return p;
18442             }
18443             p = p.parentNode;
18444         }
18445             return null;
18446     },
18447
18448     /** @ignore */
18449     onClick : function(e){
18450         var item = this.findItemFromChild(e.getTarget());
18451         if(item){
18452             var index = this.indexOf(item);
18453             if(this.onItemClick(item, index, e) !== false){
18454                 this.fireEvent("click", this, index, item, e);
18455             }
18456         }else{
18457             this.clearSelections();
18458         }
18459     },
18460
18461     /** @ignore */
18462     onContextMenu : function(e){
18463         var item = this.findItemFromChild(e.getTarget());
18464         if(item){
18465             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18466         }
18467     },
18468
18469     /** @ignore */
18470     onDblClick : function(e){
18471         var item = this.findItemFromChild(e.getTarget());
18472         if(item){
18473             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18474         }
18475     },
18476
18477     onItemClick : function(item, index, e)
18478     {
18479         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18480             return false;
18481         }
18482         if (this.toggleSelect) {
18483             var m = this.isSelected(item) ? 'unselect' : 'select';
18484             //Roo.log(m);
18485             var _t = this;
18486             _t[m](item, true, false);
18487             return true;
18488         }
18489         if(this.multiSelect || this.singleSelect){
18490             if(this.multiSelect && e.shiftKey && this.lastSelection){
18491                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18492             }else{
18493                 this.select(item, this.multiSelect && e.ctrlKey);
18494                 this.lastSelection = item;
18495             }
18496             
18497             if(!this.tickable){
18498                 e.preventDefault();
18499             }
18500             
18501         }
18502         return true;
18503     },
18504
18505     /**
18506      * Get the number of selected nodes.
18507      * @return {Number}
18508      */
18509     getSelectionCount : function(){
18510         return this.selections.length;
18511     },
18512
18513     /**
18514      * Get the currently selected nodes.
18515      * @return {Array} An array of HTMLElements
18516      */
18517     getSelectedNodes : function(){
18518         return this.selections;
18519     },
18520
18521     /**
18522      * Get the indexes of the selected nodes.
18523      * @return {Array}
18524      */
18525     getSelectedIndexes : function(){
18526         var indexes = [], s = this.selections;
18527         for(var i = 0, len = s.length; i < len; i++){
18528             indexes.push(s[i].nodeIndex);
18529         }
18530         return indexes;
18531     },
18532
18533     /**
18534      * Clear all selections
18535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18536      */
18537     clearSelections : function(suppressEvent){
18538         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18539             this.cmp.elements = this.selections;
18540             this.cmp.removeClass(this.selectedClass);
18541             this.selections = [];
18542             if(!suppressEvent){
18543                 this.fireEvent("selectionchange", this, this.selections);
18544             }
18545         }
18546     },
18547
18548     /**
18549      * Returns true if the passed node is selected
18550      * @param {HTMLElement/Number} node The node or node index
18551      * @return {Boolean}
18552      */
18553     isSelected : function(node){
18554         var s = this.selections;
18555         if(s.length < 1){
18556             return false;
18557         }
18558         node = this.getNode(node);
18559         return s.indexOf(node) !== -1;
18560     },
18561
18562     /**
18563      * Selects nodes.
18564      * @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
18565      * @param {Boolean} keepExisting (optional) true to keep existing selections
18566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18567      */
18568     select : function(nodeInfo, keepExisting, suppressEvent){
18569         if(nodeInfo instanceof Array){
18570             if(!keepExisting){
18571                 this.clearSelections(true);
18572             }
18573             for(var i = 0, len = nodeInfo.length; i < len; i++){
18574                 this.select(nodeInfo[i], true, true);
18575             }
18576             return;
18577         } 
18578         var node = this.getNode(nodeInfo);
18579         if(!node || this.isSelected(node)){
18580             return; // already selected.
18581         }
18582         if(!keepExisting){
18583             this.clearSelections(true);
18584         }
18585         
18586         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18587             Roo.fly(node).addClass(this.selectedClass);
18588             this.selections.push(node);
18589             if(!suppressEvent){
18590                 this.fireEvent("selectionchange", this, this.selections);
18591             }
18592         }
18593         
18594         
18595     },
18596       /**
18597      * Unselects nodes.
18598      * @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
18599      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18600      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18601      */
18602     unselect : function(nodeInfo, keepExisting, suppressEvent)
18603     {
18604         if(nodeInfo instanceof Array){
18605             Roo.each(this.selections, function(s) {
18606                 this.unselect(s, nodeInfo);
18607             }, this);
18608             return;
18609         }
18610         var node = this.getNode(nodeInfo);
18611         if(!node || !this.isSelected(node)){
18612             //Roo.log("not selected");
18613             return; // not selected.
18614         }
18615         // fireevent???
18616         var ns = [];
18617         Roo.each(this.selections, function(s) {
18618             if (s == node ) {
18619                 Roo.fly(node).removeClass(this.selectedClass);
18620
18621                 return;
18622             }
18623             ns.push(s);
18624         },this);
18625         
18626         this.selections= ns;
18627         this.fireEvent("selectionchange", this, this.selections);
18628     },
18629
18630     /**
18631      * Gets a template node.
18632      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18633      * @return {HTMLElement} The node or null if it wasn't found
18634      */
18635     getNode : function(nodeInfo){
18636         if(typeof nodeInfo == "string"){
18637             return document.getElementById(nodeInfo);
18638         }else if(typeof nodeInfo == "number"){
18639             return this.nodes[nodeInfo];
18640         }
18641         return nodeInfo;
18642     },
18643
18644     /**
18645      * Gets a range template nodes.
18646      * @param {Number} startIndex
18647      * @param {Number} endIndex
18648      * @return {Array} An array of nodes
18649      */
18650     getNodes : function(start, end){
18651         var ns = this.nodes;
18652         start = start || 0;
18653         end = typeof end == "undefined" ? ns.length - 1 : end;
18654         var nodes = [];
18655         if(start <= end){
18656             for(var i = start; i <= end; i++){
18657                 nodes.push(ns[i]);
18658             }
18659         } else{
18660             for(var i = start; i >= end; i--){
18661                 nodes.push(ns[i]);
18662             }
18663         }
18664         return nodes;
18665     },
18666
18667     /**
18668      * Finds the index of the passed node
18669      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18670      * @return {Number} The index of the node or -1
18671      */
18672     indexOf : function(node){
18673         node = this.getNode(node);
18674         if(typeof node.nodeIndex == "number"){
18675             return node.nodeIndex;
18676         }
18677         var ns = this.nodes;
18678         for(var i = 0, len = ns.length; i < len; i++){
18679             if(ns[i] == node){
18680                 return i;
18681             }
18682         }
18683         return -1;
18684     }
18685 });
18686 /*
18687  * - LGPL
18688  *
18689  * based on jquery fullcalendar
18690  * 
18691  */
18692
18693 Roo.bootstrap = Roo.bootstrap || {};
18694 /**
18695  * @class Roo.bootstrap.Calendar
18696  * @extends Roo.bootstrap.Component
18697  * Bootstrap Calendar class
18698  * @cfg {Boolean} loadMask (true|false) default false
18699  * @cfg {Object} header generate the user specific header of the calendar, default false
18700
18701  * @constructor
18702  * Create a new Container
18703  * @param {Object} config The config object
18704  */
18705
18706
18707
18708 Roo.bootstrap.Calendar = function(config){
18709     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18710      this.addEvents({
18711         /**
18712              * @event select
18713              * Fires when a date is selected
18714              * @param {DatePicker} this
18715              * @param {Date} date The selected date
18716              */
18717         'select': true,
18718         /**
18719              * @event monthchange
18720              * Fires when the displayed month changes 
18721              * @param {DatePicker} this
18722              * @param {Date} date The selected month
18723              */
18724         'monthchange': true,
18725         /**
18726              * @event evententer
18727              * Fires when mouse over an event
18728              * @param {Calendar} this
18729              * @param {event} Event
18730              */
18731         'evententer': true,
18732         /**
18733              * @event eventleave
18734              * Fires when the mouse leaves an
18735              * @param {Calendar} this
18736              * @param {event}
18737              */
18738         'eventleave': true,
18739         /**
18740              * @event eventclick
18741              * Fires when the mouse click an
18742              * @param {Calendar} this
18743              * @param {event}
18744              */
18745         'eventclick': true
18746         
18747     });
18748
18749 };
18750
18751 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18752     
18753      /**
18754      * @cfg {Number} startDay
18755      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18756      */
18757     startDay : 0,
18758     
18759     loadMask : false,
18760     
18761     header : false,
18762       
18763     getAutoCreate : function(){
18764         
18765         
18766         var fc_button = function(name, corner, style, content ) {
18767             return Roo.apply({},{
18768                 tag : 'span',
18769                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18770                          (corner.length ?
18771                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18772                             ''
18773                         ),
18774                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18775                 unselectable: 'on'
18776             });
18777         };
18778         
18779         var header = {};
18780         
18781         if(!this.header){
18782             header = {
18783                 tag : 'table',
18784                 cls : 'fc-header',
18785                 style : 'width:100%',
18786                 cn : [
18787                     {
18788                         tag: 'tr',
18789                         cn : [
18790                             {
18791                                 tag : 'td',
18792                                 cls : 'fc-header-left',
18793                                 cn : [
18794                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18795                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18796                                     { tag: 'span', cls: 'fc-header-space' },
18797                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18798
18799
18800                                 ]
18801                             },
18802
18803                             {
18804                                 tag : 'td',
18805                                 cls : 'fc-header-center',
18806                                 cn : [
18807                                     {
18808                                         tag: 'span',
18809                                         cls: 'fc-header-title',
18810                                         cn : {
18811                                             tag: 'H2',
18812                                             html : 'month / year'
18813                                         }
18814                                     }
18815
18816                                 ]
18817                             },
18818                             {
18819                                 tag : 'td',
18820                                 cls : 'fc-header-right',
18821                                 cn : [
18822                               /*      fc_button('month', 'left', '', 'month' ),
18823                                     fc_button('week', '', '', 'week' ),
18824                                     fc_button('day', 'right', '', 'day' )
18825                                 */    
18826
18827                                 ]
18828                             }
18829
18830                         ]
18831                     }
18832                 ]
18833             };
18834         }
18835         
18836         header = this.header;
18837         
18838        
18839         var cal_heads = function() {
18840             var ret = [];
18841             // fixme - handle this.
18842             
18843             for (var i =0; i < Date.dayNames.length; i++) {
18844                 var d = Date.dayNames[i];
18845                 ret.push({
18846                     tag: 'th',
18847                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18848                     html : d.substring(0,3)
18849                 });
18850                 
18851             }
18852             ret[0].cls += ' fc-first';
18853             ret[6].cls += ' fc-last';
18854             return ret;
18855         };
18856         var cal_cell = function(n) {
18857             return  {
18858                 tag: 'td',
18859                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18860                 cn : [
18861                     {
18862                         cn : [
18863                             {
18864                                 cls: 'fc-day-number',
18865                                 html: 'D'
18866                             },
18867                             {
18868                                 cls: 'fc-day-content',
18869                              
18870                                 cn : [
18871                                      {
18872                                         style: 'position: relative;' // height: 17px;
18873                                     }
18874                                 ]
18875                             }
18876                             
18877                             
18878                         ]
18879                     }
18880                 ]
18881                 
18882             }
18883         };
18884         var cal_rows = function() {
18885             
18886             var ret = [];
18887             for (var r = 0; r < 6; r++) {
18888                 var row= {
18889                     tag : 'tr',
18890                     cls : 'fc-week',
18891                     cn : []
18892                 };
18893                 
18894                 for (var i =0; i < Date.dayNames.length; i++) {
18895                     var d = Date.dayNames[i];
18896                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18897
18898                 }
18899                 row.cn[0].cls+=' fc-first';
18900                 row.cn[0].cn[0].style = 'min-height:90px';
18901                 row.cn[6].cls+=' fc-last';
18902                 ret.push(row);
18903                 
18904             }
18905             ret[0].cls += ' fc-first';
18906             ret[4].cls += ' fc-prev-last';
18907             ret[5].cls += ' fc-last';
18908             return ret;
18909             
18910         };
18911         
18912         var cal_table = {
18913             tag: 'table',
18914             cls: 'fc-border-separate',
18915             style : 'width:100%',
18916             cellspacing  : 0,
18917             cn : [
18918                 { 
18919                     tag: 'thead',
18920                     cn : [
18921                         { 
18922                             tag: 'tr',
18923                             cls : 'fc-first fc-last',
18924                             cn : cal_heads()
18925                         }
18926                     ]
18927                 },
18928                 { 
18929                     tag: 'tbody',
18930                     cn : cal_rows()
18931                 }
18932                   
18933             ]
18934         };
18935          
18936          var cfg = {
18937             cls : 'fc fc-ltr',
18938             cn : [
18939                 header,
18940                 {
18941                     cls : 'fc-content',
18942                     style : "position: relative;",
18943                     cn : [
18944                         {
18945                             cls : 'fc-view fc-view-month fc-grid',
18946                             style : 'position: relative',
18947                             unselectable : 'on',
18948                             cn : [
18949                                 {
18950                                     cls : 'fc-event-container',
18951                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18952                                 },
18953                                 cal_table
18954                             ]
18955                         }
18956                     ]
18957     
18958                 }
18959            ] 
18960             
18961         };
18962         
18963          
18964         
18965         return cfg;
18966     },
18967     
18968     
18969     initEvents : function()
18970     {
18971         if(!this.store){
18972             throw "can not find store for calendar";
18973         }
18974         
18975         var mark = {
18976             tag: "div",
18977             cls:"x-dlg-mask",
18978             style: "text-align:center",
18979             cn: [
18980                 {
18981                     tag: "div",
18982                     style: "background-color:white;width:50%;margin:250 auto",
18983                     cn: [
18984                         {
18985                             tag: "img",
18986                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18987                         },
18988                         {
18989                             tag: "span",
18990                             html: "Loading"
18991                         }
18992                         
18993                     ]
18994                 }
18995             ]
18996         };
18997         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18998         
18999         var size = this.el.select('.fc-content', true).first().getSize();
19000         this.maskEl.setSize(size.width, size.height);
19001         this.maskEl.enableDisplayMode("block");
19002         if(!this.loadMask){
19003             this.maskEl.hide();
19004         }
19005         
19006         this.store = Roo.factory(this.store, Roo.data);
19007         this.store.on('load', this.onLoad, this);
19008         this.store.on('beforeload', this.onBeforeLoad, this);
19009         
19010         this.resize();
19011         
19012         this.cells = this.el.select('.fc-day',true);
19013         //Roo.log(this.cells);
19014         this.textNodes = this.el.query('.fc-day-number');
19015         this.cells.addClassOnOver('fc-state-hover');
19016         
19017         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19018         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19019         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19020         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19021         
19022         this.on('monthchange', this.onMonthChange, this);
19023         
19024         this.update(new Date().clearTime());
19025     },
19026     
19027     resize : function() {
19028         var sz  = this.el.getSize();
19029         
19030         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19031         this.el.select('.fc-day-content div',true).setHeight(34);
19032     },
19033     
19034     
19035     // private
19036     showPrevMonth : function(e){
19037         this.update(this.activeDate.add("mo", -1));
19038     },
19039     showToday : function(e){
19040         this.update(new Date().clearTime());
19041     },
19042     // private
19043     showNextMonth : function(e){
19044         this.update(this.activeDate.add("mo", 1));
19045     },
19046
19047     // private
19048     showPrevYear : function(){
19049         this.update(this.activeDate.add("y", -1));
19050     },
19051
19052     // private
19053     showNextYear : function(){
19054         this.update(this.activeDate.add("y", 1));
19055     },
19056
19057     
19058    // private
19059     update : function(date)
19060     {
19061         var vd = this.activeDate;
19062         this.activeDate = date;
19063 //        if(vd && this.el){
19064 //            var t = date.getTime();
19065 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19066 //                Roo.log('using add remove');
19067 //                
19068 //                this.fireEvent('monthchange', this, date);
19069 //                
19070 //                this.cells.removeClass("fc-state-highlight");
19071 //                this.cells.each(function(c){
19072 //                   if(c.dateValue == t){
19073 //                       c.addClass("fc-state-highlight");
19074 //                       setTimeout(function(){
19075 //                            try{c.dom.firstChild.focus();}catch(e){}
19076 //                       }, 50);
19077 //                       return false;
19078 //                   }
19079 //                   return true;
19080 //                });
19081 //                return;
19082 //            }
19083 //        }
19084         
19085         var days = date.getDaysInMonth();
19086         
19087         var firstOfMonth = date.getFirstDateOfMonth();
19088         var startingPos = firstOfMonth.getDay()-this.startDay;
19089         
19090         if(startingPos < this.startDay){
19091             startingPos += 7;
19092         }
19093         
19094         var pm = date.add(Date.MONTH, -1);
19095         var prevStart = pm.getDaysInMonth()-startingPos;
19096 //        
19097         this.cells = this.el.select('.fc-day',true);
19098         this.textNodes = this.el.query('.fc-day-number');
19099         this.cells.addClassOnOver('fc-state-hover');
19100         
19101         var cells = this.cells.elements;
19102         var textEls = this.textNodes;
19103         
19104         Roo.each(cells, function(cell){
19105             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19106         });
19107         
19108         days += startingPos;
19109
19110         // convert everything to numbers so it's fast
19111         var day = 86400000;
19112         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19113         //Roo.log(d);
19114         //Roo.log(pm);
19115         //Roo.log(prevStart);
19116         
19117         var today = new Date().clearTime().getTime();
19118         var sel = date.clearTime().getTime();
19119         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19120         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19121         var ddMatch = this.disabledDatesRE;
19122         var ddText = this.disabledDatesText;
19123         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19124         var ddaysText = this.disabledDaysText;
19125         var format = this.format;
19126         
19127         var setCellClass = function(cal, cell){
19128             cell.row = 0;
19129             cell.events = [];
19130             cell.more = [];
19131             //Roo.log('set Cell Class');
19132             cell.title = "";
19133             var t = d.getTime();
19134             
19135             //Roo.log(d);
19136             
19137             cell.dateValue = t;
19138             if(t == today){
19139                 cell.className += " fc-today";
19140                 cell.className += " fc-state-highlight";
19141                 cell.title = cal.todayText;
19142             }
19143             if(t == sel){
19144                 // disable highlight in other month..
19145                 //cell.className += " fc-state-highlight";
19146                 
19147             }
19148             // disabling
19149             if(t < min) {
19150                 cell.className = " fc-state-disabled";
19151                 cell.title = cal.minText;
19152                 return;
19153             }
19154             if(t > max) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.maxText;
19157                 return;
19158             }
19159             if(ddays){
19160                 if(ddays.indexOf(d.getDay()) != -1){
19161                     cell.title = ddaysText;
19162                     cell.className = " fc-state-disabled";
19163                 }
19164             }
19165             if(ddMatch && format){
19166                 var fvalue = d.dateFormat(format);
19167                 if(ddMatch.test(fvalue)){
19168                     cell.title = ddText.replace("%0", fvalue);
19169                     cell.className = " fc-state-disabled";
19170                 }
19171             }
19172             
19173             if (!cell.initialClassName) {
19174                 cell.initialClassName = cell.dom.className;
19175             }
19176             
19177             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19178         };
19179
19180         var i = 0;
19181         
19182         for(; i < startingPos; i++) {
19183             textEls[i].innerHTML = (++prevStart);
19184             d.setDate(d.getDate()+1);
19185             
19186             cells[i].className = "fc-past fc-other-month";
19187             setCellClass(this, cells[i]);
19188         }
19189         
19190         var intDay = 0;
19191         
19192         for(; i < days; i++){
19193             intDay = i - startingPos + 1;
19194             textEls[i].innerHTML = (intDay);
19195             d.setDate(d.getDate()+1);
19196             
19197             cells[i].className = ''; // "x-date-active";
19198             setCellClass(this, cells[i]);
19199         }
19200         var extraDays = 0;
19201         
19202         for(; i < 42; i++) {
19203             textEls[i].innerHTML = (++extraDays);
19204             d.setDate(d.getDate()+1);
19205             
19206             cells[i].className = "fc-future fc-other-month";
19207             setCellClass(this, cells[i]);
19208         }
19209         
19210         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19211         
19212         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19213         
19214         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19215         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19216         
19217         if(totalRows != 6){
19218             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19219             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19220         }
19221         
19222         this.fireEvent('monthchange', this, date);
19223         
19224         
19225         /*
19226         if(!this.internalRender){
19227             var main = this.el.dom.firstChild;
19228             var w = main.offsetWidth;
19229             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19230             Roo.fly(main).setWidth(w);
19231             this.internalRender = true;
19232             // opera does not respect the auto grow header center column
19233             // then, after it gets a width opera refuses to recalculate
19234             // without a second pass
19235             if(Roo.isOpera && !this.secondPass){
19236                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19237                 this.secondPass = true;
19238                 this.update.defer(10, this, [date]);
19239             }
19240         }
19241         */
19242         
19243     },
19244     
19245     findCell : function(dt) {
19246         dt = dt.clearTime().getTime();
19247         var ret = false;
19248         this.cells.each(function(c){
19249             //Roo.log("check " +c.dateValue + '?=' + dt);
19250             if(c.dateValue == dt){
19251                 ret = c;
19252                 return false;
19253             }
19254             return true;
19255         });
19256         
19257         return ret;
19258     },
19259     
19260     findCells : function(ev) {
19261         var s = ev.start.clone().clearTime().getTime();
19262        // Roo.log(s);
19263         var e= ev.end.clone().clearTime().getTime();
19264        // Roo.log(e);
19265         var ret = [];
19266         this.cells.each(function(c){
19267              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19268             
19269             if(c.dateValue > e){
19270                 return ;
19271             }
19272             if(c.dateValue < s){
19273                 return ;
19274             }
19275             ret.push(c);
19276         });
19277         
19278         return ret;    
19279     },
19280     
19281 //    findBestRow: function(cells)
19282 //    {
19283 //        var ret = 0;
19284 //        
19285 //        for (var i =0 ; i < cells.length;i++) {
19286 //            ret  = Math.max(cells[i].rows || 0,ret);
19287 //        }
19288 //        return ret;
19289 //        
19290 //    },
19291     
19292     
19293     addItem : function(ev)
19294     {
19295         // look for vertical location slot in
19296         var cells = this.findCells(ev);
19297         
19298 //        ev.row = this.findBestRow(cells);
19299         
19300         // work out the location.
19301         
19302         var crow = false;
19303         var rows = [];
19304         for(var i =0; i < cells.length; i++) {
19305             
19306             cells[i].row = cells[0].row;
19307             
19308             if(i == 0){
19309                 cells[i].row = cells[i].row + 1;
19310             }
19311             
19312             if (!crow) {
19313                 crow = {
19314                     start : cells[i],
19315                     end :  cells[i]
19316                 };
19317                 continue;
19318             }
19319             if (crow.start.getY() == cells[i].getY()) {
19320                 // on same row.
19321                 crow.end = cells[i];
19322                 continue;
19323             }
19324             // different row.
19325             rows.push(crow);
19326             crow = {
19327                 start: cells[i],
19328                 end : cells[i]
19329             };
19330             
19331         }
19332         
19333         rows.push(crow);
19334         ev.els = [];
19335         ev.rows = rows;
19336         ev.cells = cells;
19337         
19338         cells[0].events.push(ev);
19339         
19340         this.calevents.push(ev);
19341     },
19342     
19343     clearEvents: function() {
19344         
19345         if(!this.calevents){
19346             return;
19347         }
19348         
19349         Roo.each(this.cells.elements, function(c){
19350             c.row = 0;
19351             c.events = [];
19352             c.more = [];
19353         });
19354         
19355         Roo.each(this.calevents, function(e) {
19356             Roo.each(e.els, function(el) {
19357                 el.un('mouseenter' ,this.onEventEnter, this);
19358                 el.un('mouseleave' ,this.onEventLeave, this);
19359                 el.remove();
19360             },this);
19361         },this);
19362         
19363         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19364             e.remove();
19365         });
19366         
19367     },
19368     
19369     renderEvents: function()
19370     {   
19371         var _this = this;
19372         
19373         this.cells.each(function(c) {
19374             
19375             if(c.row < 5){
19376                 return;
19377             }
19378             
19379             var ev = c.events;
19380             
19381             var r = 4;
19382             if(c.row != c.events.length){
19383                 r = 4 - (4 - (c.row - c.events.length));
19384             }
19385             
19386             c.events = ev.slice(0, r);
19387             c.more = ev.slice(r);
19388             
19389             if(c.more.length && c.more.length == 1){
19390                 c.events.push(c.more.pop());
19391             }
19392             
19393             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19394             
19395         });
19396             
19397         this.cells.each(function(c) {
19398             
19399             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19400             
19401             
19402             for (var e = 0; e < c.events.length; e++){
19403                 var ev = c.events[e];
19404                 var rows = ev.rows;
19405                 
19406                 for(var i = 0; i < rows.length; i++) {
19407                 
19408                     // how many rows should it span..
19409
19410                     var  cfg = {
19411                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19412                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19413
19414                         unselectable : "on",
19415                         cn : [
19416                             {
19417                                 cls: 'fc-event-inner',
19418                                 cn : [
19419     //                                {
19420     //                                  tag:'span',
19421     //                                  cls: 'fc-event-time',
19422     //                                  html : cells.length > 1 ? '' : ev.time
19423     //                                },
19424                                     {
19425                                       tag:'span',
19426                                       cls: 'fc-event-title',
19427                                       html : String.format('{0}', ev.title)
19428                                     }
19429
19430
19431                                 ]
19432                             },
19433                             {
19434                                 cls: 'ui-resizable-handle ui-resizable-e',
19435                                 html : '&nbsp;&nbsp;&nbsp'
19436                             }
19437
19438                         ]
19439                     };
19440
19441                     if (i == 0) {
19442                         cfg.cls += ' fc-event-start';
19443                     }
19444                     if ((i+1) == rows.length) {
19445                         cfg.cls += ' fc-event-end';
19446                     }
19447
19448                     var ctr = _this.el.select('.fc-event-container',true).first();
19449                     var cg = ctr.createChild(cfg);
19450
19451                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19452                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19453
19454                     var r = (c.more.length) ? 1 : 0;
19455                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19456                     cg.setWidth(ebox.right - sbox.x -2);
19457
19458                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19459                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19460                     cg.on('click', _this.onEventClick, _this, ev);
19461
19462                     ev.els.push(cg);
19463                     
19464                 }
19465                 
19466             }
19467             
19468             
19469             if(c.more.length){
19470                 var  cfg = {
19471                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19472                     style : 'position: absolute',
19473                     unselectable : "on",
19474                     cn : [
19475                         {
19476                             cls: 'fc-event-inner',
19477                             cn : [
19478                                 {
19479                                   tag:'span',
19480                                   cls: 'fc-event-title',
19481                                   html : 'More'
19482                                 }
19483
19484
19485                             ]
19486                         },
19487                         {
19488                             cls: 'ui-resizable-handle ui-resizable-e',
19489                             html : '&nbsp;&nbsp;&nbsp'
19490                         }
19491
19492                     ]
19493                 };
19494
19495                 var ctr = _this.el.select('.fc-event-container',true).first();
19496                 var cg = ctr.createChild(cfg);
19497
19498                 var sbox = c.select('.fc-day-content',true).first().getBox();
19499                 var ebox = c.select('.fc-day-content',true).first().getBox();
19500                 //Roo.log(cg);
19501                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19502                 cg.setWidth(ebox.right - sbox.x -2);
19503
19504                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19505                 
19506             }
19507             
19508         });
19509         
19510         
19511         
19512     },
19513     
19514     onEventEnter: function (e, el,event,d) {
19515         this.fireEvent('evententer', this, el, event);
19516     },
19517     
19518     onEventLeave: function (e, el,event,d) {
19519         this.fireEvent('eventleave', this, el, event);
19520     },
19521     
19522     onEventClick: function (e, el,event,d) {
19523         this.fireEvent('eventclick', this, el, event);
19524     },
19525     
19526     onMonthChange: function () {
19527         this.store.load();
19528     },
19529     
19530     onMoreEventClick: function(e, el, more)
19531     {
19532         var _this = this;
19533         
19534         this.calpopover.placement = 'right';
19535         this.calpopover.setTitle('More');
19536         
19537         this.calpopover.setContent('');
19538         
19539         var ctr = this.calpopover.el.select('.popover-content', true).first();
19540         
19541         Roo.each(more, function(m){
19542             var cfg = {
19543                 cls : 'fc-event-hori fc-event-draggable',
19544                 html : m.title
19545             };
19546             var cg = ctr.createChild(cfg);
19547             
19548             cg.on('click', _this.onEventClick, _this, m);
19549         });
19550         
19551         this.calpopover.show(el);
19552         
19553         
19554     },
19555     
19556     onLoad: function () 
19557     {   
19558         this.calevents = [];
19559         var cal = this;
19560         
19561         if(this.store.getCount() > 0){
19562             this.store.data.each(function(d){
19563                cal.addItem({
19564                     id : d.data.id,
19565                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19566                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19567                     time : d.data.start_time,
19568                     title : d.data.title,
19569                     description : d.data.description,
19570                     venue : d.data.venue
19571                 });
19572             });
19573         }
19574         
19575         this.renderEvents();
19576         
19577         if(this.calevents.length && this.loadMask){
19578             this.maskEl.hide();
19579         }
19580     },
19581     
19582     onBeforeLoad: function()
19583     {
19584         this.clearEvents();
19585         if(this.loadMask){
19586             this.maskEl.show();
19587         }
19588     }
19589 });
19590
19591  
19592  /*
19593  * - LGPL
19594  *
19595  * element
19596  * 
19597  */
19598
19599 /**
19600  * @class Roo.bootstrap.Popover
19601  * @extends Roo.bootstrap.Component
19602  * Bootstrap Popover class
19603  * @cfg {String} html contents of the popover   (or false to use children..)
19604  * @cfg {String} title of popover (or false to hide)
19605  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19606  * @cfg {String} trigger click || hover (or false to trigger manually)
19607  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19608  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19609  *      - if false and it has a 'parent' then it will be automatically added to that element
19610  *      - if string - Roo.get  will be called 
19611  * @cfg {Number} delay - delay before showing
19612  
19613  * @constructor
19614  * Create a new Popover
19615  * @param {Object} config The config object
19616  */
19617
19618 Roo.bootstrap.Popover = function(config){
19619     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19620     
19621     this.addEvents({
19622         // raw events
19623          /**
19624          * @event show
19625          * After the popover show
19626          * 
19627          * @param {Roo.bootstrap.Popover} this
19628          */
19629         "show" : true,
19630         /**
19631          * @event hide
19632          * After the popover hide
19633          * 
19634          * @param {Roo.bootstrap.Popover} this
19635          */
19636         "hide" : true
19637     });
19638 };
19639
19640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19641     
19642     title: false,
19643     html: false,
19644     
19645     placement : 'right',
19646     trigger : 'hover', // hover
19647     
19648     delay : 0,
19649     
19650     over: false,
19651     
19652     can_build_overlaid : false,
19653     
19654     getChildContainer : function()
19655     {
19656         return this.el.select('.popover-content',true).first();
19657     },
19658     
19659     getAutoCreate : function(){
19660          
19661         var cfg = {
19662            cls : 'popover roo-dynamic shadow roo-popover',
19663            style: 'display:block',
19664            cn : [
19665                 {
19666                     cls : 'arrow'
19667                 },
19668                 {
19669                     cls : 'popover-inner ',
19670                     cn : [
19671                         {
19672                             tag: 'h3',
19673                             cls: 'popover-title popover-header',
19674                             html : this.title || ''
19675                         },
19676                         {
19677                             cls : 'popover-content popover-body'  + this.cls,
19678                             html : this.html || ''
19679                         }
19680                     ]
19681                     
19682                 }
19683            ]
19684         };
19685         
19686         return cfg;
19687     },
19688     /**
19689      * @param {string} the title
19690      */
19691     setTitle: function(str)
19692     {
19693         this.title = str;
19694         if (this.el) {
19695             this.el.select('.popover-title',true).first().dom.innerHTML = str;
19696         }
19697         
19698     },
19699     /**
19700      * @param {string} the body content
19701      */
19702     setContent: function(str)
19703     {
19704         this.html = str;
19705         if (this.el) {
19706             this.el.select('.popover-content',true).first().dom.innerHTML = str;
19707         }
19708         
19709     },
19710     // as it get's added to the bottom of the page.
19711     onRender : function(ct, position)
19712     {
19713         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19714         if(!this.el){
19715             var cfg = Roo.apply({},  this.getAutoCreate());
19716             cfg.id = Roo.id();
19717             
19718             if (this.cls) {
19719                 cfg.cls += ' ' + this.cls;
19720             }
19721             if (this.style) {
19722                 cfg.style = this.style;
19723             }
19724             //Roo.log("adding to ");
19725             this.el = Roo.get(document.body).createChild(cfg, position);
19726 //            Roo.log(this.el);
19727         }
19728         
19729         var nitems = [];
19730         if(typeof(this.items) != 'undefined'){
19731             var items = this.items;
19732             delete this.items;
19733
19734             for(var i =0;i < items.length;i++) {
19735                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19736             }
19737         }
19738
19739         this.items = nitems;
19740         
19741         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19742
19743         
19744         
19745         this.initEvents();
19746     },
19747     
19748     initEvents : function()
19749     {
19750         
19751         Roo.bootstrap.Popover.register(this);
19752         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19753         this.el.enableDisplayMode('block');
19754         this.el.hide();
19755         if (this.over === false && !this.parent()) {
19756             return; 
19757         }
19758         if (this.triggers === false) {
19759             return;
19760         }
19761          
19762         // support parent
19763         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19764         var triggers = this.trigger ? this.trigger.split(' ') : [];
19765         Roo.each(triggers, function(trigger) {
19766         
19767             if (trigger == 'click') {
19768                 on_el.on('click', this.toggle, this);
19769             } else if (trigger != 'manual') {
19770                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19771                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19772       
19773                 on_el.on(eventIn  ,this.enter, this);
19774                 on_el.on(eventOut, this.leave, this);
19775             }
19776         }, this);
19777         
19778     },
19779     
19780     
19781     // private
19782     timeout : null,
19783     hoverState : null,
19784     
19785     toggle : function () {
19786         this.hoverState == 'in' ? this.leave() : this.enter();
19787     },
19788     
19789     enter : function () {
19790         
19791         clearTimeout(this.timeout);
19792     
19793         this.hoverState = 'in';
19794     
19795         if (!this.delay || !this.delay.show) {
19796             this.show();
19797             return;
19798         }
19799         var _t = this;
19800         this.timeout = setTimeout(function () {
19801             if (_t.hoverState == 'in') {
19802                 _t.show();
19803             }
19804         }, this.delay.show)
19805     },
19806     
19807     leave : function() {
19808         clearTimeout(this.timeout);
19809     
19810         this.hoverState = 'out';
19811     
19812         if (!this.delay || !this.delay.hide) {
19813             this.hide();
19814             return;
19815         }
19816         var _t = this;
19817         this.timeout = setTimeout(function () {
19818             if (_t.hoverState == 'out') {
19819                 _t.hide();
19820             }
19821         }, this.delay.hide)
19822     },
19823     /**
19824      * Show the popover
19825      * @param {Roo.Element|string|false} - element to align and point to.
19826      */
19827     show : function (on_el)
19828     {
19829         
19830         on_el = on_el || false; // default to false
19831         if (!on_el) {
19832             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19833                 on_el = this.parent().el;
19834             } else if (this.over) {
19835                 Roo.get(this.over);
19836             }
19837             
19838         }
19839         
19840         if (!this.el) {
19841             this.render(document.body);
19842         }
19843         
19844         
19845         this.el.removeClass([
19846             'fade','top','bottom', 'left', 'right','in',
19847             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19848         ]);
19849         
19850         if (!this.title.length) {
19851             this.el.select('.popover-title',true).hide();
19852         }
19853         
19854         
19855         var placement = typeof this.placement == 'function' ?
19856             this.placement.call(this, this.el, on_el) :
19857             this.placement;
19858             
19859         /*
19860         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19861         
19862         // I think  'auto right' - but 
19863         
19864         var autoPlace = autoToken.test(placement);
19865         if (autoPlace) {
19866             placement = placement.replace(autoToken, '') || 'top';
19867         }
19868         */
19869         
19870         
19871         this.el.show();
19872         this.el.dom.style.display='block';
19873         
19874         //this.el.appendTo(on_el);
19875         
19876         var p = this.getPosition();
19877         var box = this.el.getBox();
19878         
19879         
19880         var align = Roo.bootstrap.Popover.alignment[placement];
19881         this.el.addClass(align[2]);
19882
19883 //        Roo.log(align);
19884
19885         if (on_el) {
19886             this.el.alignTo(on_el, align[0],align[1]);
19887         } else {
19888             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19889             var es = this.el.getSize();
19890             var x = Roo.lib.Dom.getViewWidth()/2;
19891             var y = Roo.lib.Dom.getViewHeight()/2;
19892             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19893             
19894         }
19895
19896         
19897         //var arrow = this.el.select('.arrow',true).first();
19898         //arrow.set(align[2], 
19899         
19900         this.el.addClass('in');
19901         
19902         
19903         if (this.el.hasClass('fade')) {
19904             // fade it?
19905         }
19906         
19907         this.hoverState = 'in';
19908         
19909         this.fireEvent('show', this);
19910         
19911     },
19912     hide : function()
19913     {
19914         this.el.setXY([0,0]);
19915         this.el.removeClass('in');
19916         this.el.hide();
19917         this.hoverState = null;
19918         
19919         this.fireEvent('hide', this);
19920     }
19921     
19922 });
19923
19924
19925 Roo.apply(Roo.bootstrap.Popover, {
19926
19927     alignment : {
19928         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19929         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19930         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19931         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19932     },
19933
19934     clickHander : false,
19935     
19936
19937     onMouseDown : function(e)
19938     {
19939         if (!e.getTarget(".roo-popover")) {
19940             this.hideAll();
19941         }
19942          
19943     },
19944     
19945     popups : [],
19946     
19947     register : function(popup)
19948     {
19949         if (!Roo.bootstrap.Popover.clickHandler) {
19950             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19951         }
19952         // hide other popups.
19953         this.hideAll();
19954         this.popups.push(popup);
19955     },
19956     hideAll : function()
19957     {
19958         this.popups.forEach(function(p) {
19959             p.hide();
19960         });
19961     }
19962
19963 });/*
19964  * - LGPL
19965  *
19966  * Progress
19967  * 
19968  */
19969
19970 /**
19971  * @class Roo.bootstrap.Progress
19972  * @extends Roo.bootstrap.Component
19973  * Bootstrap Progress class
19974  * @cfg {Boolean} striped striped of the progress bar
19975  * @cfg {Boolean} active animated of the progress bar
19976  * 
19977  * 
19978  * @constructor
19979  * Create a new Progress
19980  * @param {Object} config The config object
19981  */
19982
19983 Roo.bootstrap.Progress = function(config){
19984     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19985 };
19986
19987 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19988     
19989     striped : false,
19990     active: false,
19991     
19992     getAutoCreate : function(){
19993         var cfg = {
19994             tag: 'div',
19995             cls: 'progress'
19996         };
19997         
19998         
19999         if(this.striped){
20000             cfg.cls += ' progress-striped';
20001         }
20002       
20003         if(this.active){
20004             cfg.cls += ' active';
20005         }
20006         
20007         
20008         return cfg;
20009     }
20010    
20011 });
20012
20013  
20014
20015  /*
20016  * - LGPL
20017  *
20018  * ProgressBar
20019  * 
20020  */
20021
20022 /**
20023  * @class Roo.bootstrap.ProgressBar
20024  * @extends Roo.bootstrap.Component
20025  * Bootstrap ProgressBar class
20026  * @cfg {Number} aria_valuenow aria-value now
20027  * @cfg {Number} aria_valuemin aria-value min
20028  * @cfg {Number} aria_valuemax aria-value max
20029  * @cfg {String} label label for the progress bar
20030  * @cfg {String} panel (success | info | warning | danger )
20031  * @cfg {String} role role of the progress bar
20032  * @cfg {String} sr_only text
20033  * 
20034  * 
20035  * @constructor
20036  * Create a new ProgressBar
20037  * @param {Object} config The config object
20038  */
20039
20040 Roo.bootstrap.ProgressBar = function(config){
20041     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20042 };
20043
20044 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20045     
20046     aria_valuenow : 0,
20047     aria_valuemin : 0,
20048     aria_valuemax : 100,
20049     label : false,
20050     panel : false,
20051     role : false,
20052     sr_only: false,
20053     
20054     getAutoCreate : function()
20055     {
20056         
20057         var cfg = {
20058             tag: 'div',
20059             cls: 'progress-bar',
20060             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20061         };
20062         
20063         if(this.sr_only){
20064             cfg.cn = {
20065                 tag: 'span',
20066                 cls: 'sr-only',
20067                 html: this.sr_only
20068             }
20069         }
20070         
20071         if(this.role){
20072             cfg.role = this.role;
20073         }
20074         
20075         if(this.aria_valuenow){
20076             cfg['aria-valuenow'] = this.aria_valuenow;
20077         }
20078         
20079         if(this.aria_valuemin){
20080             cfg['aria-valuemin'] = this.aria_valuemin;
20081         }
20082         
20083         if(this.aria_valuemax){
20084             cfg['aria-valuemax'] = this.aria_valuemax;
20085         }
20086         
20087         if(this.label && !this.sr_only){
20088             cfg.html = this.label;
20089         }
20090         
20091         if(this.panel){
20092             cfg.cls += ' progress-bar-' + this.panel;
20093         }
20094         
20095         return cfg;
20096     },
20097     
20098     update : function(aria_valuenow)
20099     {
20100         this.aria_valuenow = aria_valuenow;
20101         
20102         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20103     }
20104    
20105 });
20106
20107  
20108
20109  /*
20110  * - LGPL
20111  *
20112  * column
20113  * 
20114  */
20115
20116 /**
20117  * @class Roo.bootstrap.TabGroup
20118  * @extends Roo.bootstrap.Column
20119  * Bootstrap Column class
20120  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20121  * @cfg {Boolean} carousel true to make the group behave like a carousel
20122  * @cfg {Boolean} bullets show bullets for the panels
20123  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20124  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20125  * @cfg {Boolean} showarrow (true|false) show arrow default true
20126  * 
20127  * @constructor
20128  * Create a new TabGroup
20129  * @param {Object} config The config object
20130  */
20131
20132 Roo.bootstrap.TabGroup = function(config){
20133     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20134     if (!this.navId) {
20135         this.navId = Roo.id();
20136     }
20137     this.tabs = [];
20138     Roo.bootstrap.TabGroup.register(this);
20139     
20140 };
20141
20142 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20143     
20144     carousel : false,
20145     transition : false,
20146     bullets : 0,
20147     timer : 0,
20148     autoslide : false,
20149     slideFn : false,
20150     slideOnTouch : false,
20151     showarrow : true,
20152     
20153     getAutoCreate : function()
20154     {
20155         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20156         
20157         cfg.cls += ' tab-content';
20158         
20159         if (this.carousel) {
20160             cfg.cls += ' carousel slide';
20161             
20162             cfg.cn = [{
20163                cls : 'carousel-inner',
20164                cn : []
20165             }];
20166         
20167             if(this.bullets  && !Roo.isTouch){
20168                 
20169                 var bullets = {
20170                     cls : 'carousel-bullets',
20171                     cn : []
20172                 };
20173                
20174                 if(this.bullets_cls){
20175                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20176                 }
20177                 
20178                 bullets.cn.push({
20179                     cls : 'clear'
20180                 });
20181                 
20182                 cfg.cn[0].cn.push(bullets);
20183             }
20184             
20185             if(this.showarrow){
20186                 cfg.cn[0].cn.push({
20187                     tag : 'div',
20188                     class : 'carousel-arrow',
20189                     cn : [
20190                         {
20191                             tag : 'div',
20192                             class : 'carousel-prev',
20193                             cn : [
20194                                 {
20195                                     tag : 'i',
20196                                     class : 'fa fa-chevron-left'
20197                                 }
20198                             ]
20199                         },
20200                         {
20201                             tag : 'div',
20202                             class : 'carousel-next',
20203                             cn : [
20204                                 {
20205                                     tag : 'i',
20206                                     class : 'fa fa-chevron-right'
20207                                 }
20208                             ]
20209                         }
20210                     ]
20211                 });
20212             }
20213             
20214         }
20215         
20216         return cfg;
20217     },
20218     
20219     initEvents:  function()
20220     {
20221 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20222 //            this.el.on("touchstart", this.onTouchStart, this);
20223 //        }
20224         
20225         if(this.autoslide){
20226             var _this = this;
20227             
20228             this.slideFn = window.setInterval(function() {
20229                 _this.showPanelNext();
20230             }, this.timer);
20231         }
20232         
20233         if(this.showarrow){
20234             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20235             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20236         }
20237         
20238         
20239     },
20240     
20241 //    onTouchStart : function(e, el, o)
20242 //    {
20243 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20244 //            return;
20245 //        }
20246 //        
20247 //        this.showPanelNext();
20248 //    },
20249     
20250     
20251     getChildContainer : function()
20252     {
20253         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20254     },
20255     
20256     /**
20257     * register a Navigation item
20258     * @param {Roo.bootstrap.NavItem} the navitem to add
20259     */
20260     register : function(item)
20261     {
20262         this.tabs.push( item);
20263         item.navId = this.navId; // not really needed..
20264         this.addBullet();
20265     
20266     },
20267     
20268     getActivePanel : function()
20269     {
20270         var r = false;
20271         Roo.each(this.tabs, function(t) {
20272             if (t.active) {
20273                 r = t;
20274                 return false;
20275             }
20276             return null;
20277         });
20278         return r;
20279         
20280     },
20281     getPanelByName : function(n)
20282     {
20283         var r = false;
20284         Roo.each(this.tabs, function(t) {
20285             if (t.tabId == n) {
20286                 r = t;
20287                 return false;
20288             }
20289             return null;
20290         });
20291         return r;
20292     },
20293     indexOfPanel : function(p)
20294     {
20295         var r = false;
20296         Roo.each(this.tabs, function(t,i) {
20297             if (t.tabId == p.tabId) {
20298                 r = i;
20299                 return false;
20300             }
20301             return null;
20302         });
20303         return r;
20304     },
20305     /**
20306      * show a specific panel
20307      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20308      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20309      */
20310     showPanel : function (pan)
20311     {
20312         if(this.transition || typeof(pan) == 'undefined'){
20313             Roo.log("waiting for the transitionend");
20314             return false;
20315         }
20316         
20317         if (typeof(pan) == 'number') {
20318             pan = this.tabs[pan];
20319         }
20320         
20321         if (typeof(pan) == 'string') {
20322             pan = this.getPanelByName(pan);
20323         }
20324         
20325         var cur = this.getActivePanel();
20326         
20327         if(!pan || !cur){
20328             Roo.log('pan or acitve pan is undefined');
20329             return false;
20330         }
20331         
20332         if (pan.tabId == this.getActivePanel().tabId) {
20333             return true;
20334         }
20335         
20336         if (false === cur.fireEvent('beforedeactivate')) {
20337             return false;
20338         }
20339         
20340         if(this.bullets > 0 && !Roo.isTouch){
20341             this.setActiveBullet(this.indexOfPanel(pan));
20342         }
20343         
20344         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20345             
20346             //class="carousel-item carousel-item-next carousel-item-left"
20347             
20348             this.transition = true;
20349             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20350             var lr = dir == 'next' ? 'left' : 'right';
20351             pan.el.addClass(dir); // or prev
20352             pan.el.addClass('carousel-item-' + dir); // or prev
20353             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20354             cur.el.addClass(lr); // or right
20355             pan.el.addClass(lr);
20356             cur.el.addClass('carousel-item-' +lr); // or right
20357             pan.el.addClass('carousel-item-' +lr);
20358             
20359             
20360             var _this = this;
20361             cur.el.on('transitionend', function() {
20362                 Roo.log("trans end?");
20363                 
20364                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20365                 pan.setActive(true);
20366                 
20367                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20368                 cur.setActive(false);
20369                 
20370                 _this.transition = false;
20371                 
20372             }, this, { single:  true } );
20373             
20374             return true;
20375         }
20376         
20377         cur.setActive(false);
20378         pan.setActive(true);
20379         
20380         return true;
20381         
20382     },
20383     showPanelNext : function()
20384     {
20385         var i = this.indexOfPanel(this.getActivePanel());
20386         
20387         if (i >= this.tabs.length - 1 && !this.autoslide) {
20388             return;
20389         }
20390         
20391         if (i >= this.tabs.length - 1 && this.autoslide) {
20392             i = -1;
20393         }
20394         
20395         this.showPanel(this.tabs[i+1]);
20396     },
20397     
20398     showPanelPrev : function()
20399     {
20400         var i = this.indexOfPanel(this.getActivePanel());
20401         
20402         if (i  < 1 && !this.autoslide) {
20403             return;
20404         }
20405         
20406         if (i < 1 && this.autoslide) {
20407             i = this.tabs.length;
20408         }
20409         
20410         this.showPanel(this.tabs[i-1]);
20411     },
20412     
20413     
20414     addBullet: function()
20415     {
20416         if(!this.bullets || Roo.isTouch){
20417             return;
20418         }
20419         var ctr = this.el.select('.carousel-bullets',true).first();
20420         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20421         var bullet = ctr.createChild({
20422             cls : 'bullet bullet-' + i
20423         },ctr.dom.lastChild);
20424         
20425         
20426         var _this = this;
20427         
20428         bullet.on('click', (function(e, el, o, ii, t){
20429
20430             e.preventDefault();
20431
20432             this.showPanel(ii);
20433
20434             if(this.autoslide && this.slideFn){
20435                 clearInterval(this.slideFn);
20436                 this.slideFn = window.setInterval(function() {
20437                     _this.showPanelNext();
20438                 }, this.timer);
20439             }
20440
20441         }).createDelegate(this, [i, bullet], true));
20442                 
20443         
20444     },
20445      
20446     setActiveBullet : function(i)
20447     {
20448         if(Roo.isTouch){
20449             return;
20450         }
20451         
20452         Roo.each(this.el.select('.bullet', true).elements, function(el){
20453             el.removeClass('selected');
20454         });
20455
20456         var bullet = this.el.select('.bullet-' + i, true).first();
20457         
20458         if(!bullet){
20459             return;
20460         }
20461         
20462         bullet.addClass('selected');
20463     }
20464     
20465     
20466   
20467 });
20468
20469  
20470
20471  
20472  
20473 Roo.apply(Roo.bootstrap.TabGroup, {
20474     
20475     groups: {},
20476      /**
20477     * register a Navigation Group
20478     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20479     */
20480     register : function(navgrp)
20481     {
20482         this.groups[navgrp.navId] = navgrp;
20483         
20484     },
20485     /**
20486     * fetch a Navigation Group based on the navigation ID
20487     * if one does not exist , it will get created.
20488     * @param {string} the navgroup to add
20489     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20490     */
20491     get: function(navId) {
20492         if (typeof(this.groups[navId]) == 'undefined') {
20493             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20494         }
20495         return this.groups[navId] ;
20496     }
20497     
20498     
20499     
20500 });
20501
20502  /*
20503  * - LGPL
20504  *
20505  * TabPanel
20506  * 
20507  */
20508
20509 /**
20510  * @class Roo.bootstrap.TabPanel
20511  * @extends Roo.bootstrap.Component
20512  * Bootstrap TabPanel class
20513  * @cfg {Boolean} active panel active
20514  * @cfg {String} html panel content
20515  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20516  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20517  * @cfg {String} href click to link..
20518  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20519  * 
20520  * 
20521  * @constructor
20522  * Create a new TabPanel
20523  * @param {Object} config The config object
20524  */
20525
20526 Roo.bootstrap.TabPanel = function(config){
20527     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20528     this.addEvents({
20529         /**
20530              * @event changed
20531              * Fires when the active status changes
20532              * @param {Roo.bootstrap.TabPanel} this
20533              * @param {Boolean} state the new state
20534             
20535          */
20536         'changed': true,
20537         /**
20538              * @event beforedeactivate
20539              * Fires before a tab is de-activated - can be used to do validation on a form.
20540              * @param {Roo.bootstrap.TabPanel} this
20541              * @return {Boolean} false if there is an error
20542             
20543          */
20544         'beforedeactivate': true
20545      });
20546     
20547     this.tabId = this.tabId || Roo.id();
20548   
20549 };
20550
20551 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20552     
20553     active: false,
20554     html: false,
20555     tabId: false,
20556     navId : false,
20557     href : '',
20558     touchSlide : false,
20559     getAutoCreate : function(){
20560         
20561         
20562         var cfg = {
20563             tag: 'div',
20564             // item is needed for carousel - not sure if it has any effect otherwise
20565             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20566             html: this.html || ''
20567         };
20568         
20569         if(this.active){
20570             cfg.cls += ' active';
20571         }
20572         
20573         if(this.tabId){
20574             cfg.tabId = this.tabId;
20575         }
20576         
20577         
20578         
20579         return cfg;
20580     },
20581     
20582     initEvents:  function()
20583     {
20584         var p = this.parent();
20585         
20586         this.navId = this.navId || p.navId;
20587         
20588         if (typeof(this.navId) != 'undefined') {
20589             // not really needed.. but just in case.. parent should be a NavGroup.
20590             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20591             
20592             tg.register(this);
20593             
20594             var i = tg.tabs.length - 1;
20595             
20596             if(this.active && tg.bullets > 0 && i < tg.bullets){
20597                 tg.setActiveBullet(i);
20598             }
20599         }
20600         
20601         this.el.on('click', this.onClick, this);
20602         
20603         if(Roo.isTouch && this.touchSlide){
20604             this.el.on("touchstart", this.onTouchStart, this);
20605             this.el.on("touchmove", this.onTouchMove, this);
20606             this.el.on("touchend", this.onTouchEnd, this);
20607         }
20608         
20609     },
20610     
20611     onRender : function(ct, position)
20612     {
20613         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20614     },
20615     
20616     setActive : function(state)
20617     {
20618         Roo.log("panel - set active " + this.tabId + "=" + state);
20619         
20620         this.active = state;
20621         if (!state) {
20622             this.el.removeClass('active');
20623             
20624         } else  if (!this.el.hasClass('active')) {
20625             this.el.addClass('active');
20626         }
20627         
20628         this.fireEvent('changed', this, state);
20629     },
20630     
20631     onClick : function(e)
20632     {
20633         e.preventDefault();
20634         
20635         if(!this.href.length){
20636             return;
20637         }
20638         
20639         window.location.href = this.href;
20640     },
20641     
20642     startX : 0,
20643     startY : 0,
20644     endX : 0,
20645     endY : 0,
20646     swiping : false,
20647     
20648     onTouchStart : function(e)
20649     {
20650         this.swiping = false;
20651         
20652         this.startX = e.browserEvent.touches[0].clientX;
20653         this.startY = e.browserEvent.touches[0].clientY;
20654     },
20655     
20656     onTouchMove : function(e)
20657     {
20658         this.swiping = true;
20659         
20660         this.endX = e.browserEvent.touches[0].clientX;
20661         this.endY = e.browserEvent.touches[0].clientY;
20662     },
20663     
20664     onTouchEnd : function(e)
20665     {
20666         if(!this.swiping){
20667             this.onClick(e);
20668             return;
20669         }
20670         
20671         var tabGroup = this.parent();
20672         
20673         if(this.endX > this.startX){ // swiping right
20674             tabGroup.showPanelPrev();
20675             return;
20676         }
20677         
20678         if(this.startX > this.endX){ // swiping left
20679             tabGroup.showPanelNext();
20680             return;
20681         }
20682     }
20683     
20684     
20685 });
20686  
20687
20688  
20689
20690  /*
20691  * - LGPL
20692  *
20693  * DateField
20694  * 
20695  */
20696
20697 /**
20698  * @class Roo.bootstrap.DateField
20699  * @extends Roo.bootstrap.Input
20700  * Bootstrap DateField class
20701  * @cfg {Number} weekStart default 0
20702  * @cfg {String} viewMode default empty, (months|years)
20703  * @cfg {String} minViewMode default empty, (months|years)
20704  * @cfg {Number} startDate default -Infinity
20705  * @cfg {Number} endDate default Infinity
20706  * @cfg {Boolean} todayHighlight default false
20707  * @cfg {Boolean} todayBtn default false
20708  * @cfg {Boolean} calendarWeeks default false
20709  * @cfg {Object} daysOfWeekDisabled default empty
20710  * @cfg {Boolean} singleMode default false (true | false)
20711  * 
20712  * @cfg {Boolean} keyboardNavigation default true
20713  * @cfg {String} language default en
20714  * 
20715  * @constructor
20716  * Create a new DateField
20717  * @param {Object} config The config object
20718  */
20719
20720 Roo.bootstrap.DateField = function(config){
20721     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20722      this.addEvents({
20723             /**
20724              * @event show
20725              * Fires when this field show.
20726              * @param {Roo.bootstrap.DateField} this
20727              * @param {Mixed} date The date value
20728              */
20729             show : true,
20730             /**
20731              * @event show
20732              * Fires when this field hide.
20733              * @param {Roo.bootstrap.DateField} this
20734              * @param {Mixed} date The date value
20735              */
20736             hide : true,
20737             /**
20738              * @event select
20739              * Fires when select a date.
20740              * @param {Roo.bootstrap.DateField} this
20741              * @param {Mixed} date The date value
20742              */
20743             select : true,
20744             /**
20745              * @event beforeselect
20746              * Fires when before select a date.
20747              * @param {Roo.bootstrap.DateField} this
20748              * @param {Mixed} date The date value
20749              */
20750             beforeselect : true
20751         });
20752 };
20753
20754 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20755     
20756     /**
20757      * @cfg {String} format
20758      * The default date format string which can be overriden for localization support.  The format must be
20759      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20760      */
20761     format : "m/d/y",
20762     /**
20763      * @cfg {String} altFormats
20764      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20765      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20766      */
20767     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20768     
20769     weekStart : 0,
20770     
20771     viewMode : '',
20772     
20773     minViewMode : '',
20774     
20775     todayHighlight : false,
20776     
20777     todayBtn: false,
20778     
20779     language: 'en',
20780     
20781     keyboardNavigation: true,
20782     
20783     calendarWeeks: false,
20784     
20785     startDate: -Infinity,
20786     
20787     endDate: Infinity,
20788     
20789     daysOfWeekDisabled: [],
20790     
20791     _events: [],
20792     
20793     singleMode : false,
20794     
20795     UTCDate: function()
20796     {
20797         return new Date(Date.UTC.apply(Date, arguments));
20798     },
20799     
20800     UTCToday: function()
20801     {
20802         var today = new Date();
20803         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20804     },
20805     
20806     getDate: function() {
20807             var d = this.getUTCDate();
20808             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20809     },
20810     
20811     getUTCDate: function() {
20812             return this.date;
20813     },
20814     
20815     setDate: function(d) {
20816             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20817     },
20818     
20819     setUTCDate: function(d) {
20820             this.date = d;
20821             this.setValue(this.formatDate(this.date));
20822     },
20823         
20824     onRender: function(ct, position)
20825     {
20826         
20827         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20828         
20829         this.language = this.language || 'en';
20830         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20831         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20832         
20833         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20834         this.format = this.format || 'm/d/y';
20835         this.isInline = false;
20836         this.isInput = true;
20837         this.component = this.el.select('.add-on', true).first() || false;
20838         this.component = (this.component && this.component.length === 0) ? false : this.component;
20839         this.hasInput = this.component && this.inputEl().length;
20840         
20841         if (typeof(this.minViewMode === 'string')) {
20842             switch (this.minViewMode) {
20843                 case 'months':
20844                     this.minViewMode = 1;
20845                     break;
20846                 case 'years':
20847                     this.minViewMode = 2;
20848                     break;
20849                 default:
20850                     this.minViewMode = 0;
20851                     break;
20852             }
20853         }
20854         
20855         if (typeof(this.viewMode === 'string')) {
20856             switch (this.viewMode) {
20857                 case 'months':
20858                     this.viewMode = 1;
20859                     break;
20860                 case 'years':
20861                     this.viewMode = 2;
20862                     break;
20863                 default:
20864                     this.viewMode = 0;
20865                     break;
20866             }
20867         }
20868                 
20869         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20870         
20871 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20872         
20873         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20874         
20875         this.picker().on('mousedown', this.onMousedown, this);
20876         this.picker().on('click', this.onClick, this);
20877         
20878         this.picker().addClass('datepicker-dropdown');
20879         
20880         this.startViewMode = this.viewMode;
20881         
20882         if(this.singleMode){
20883             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20884                 v.setVisibilityMode(Roo.Element.DISPLAY);
20885                 v.hide();
20886             });
20887             
20888             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20889                 v.setStyle('width', '189px');
20890             });
20891         }
20892         
20893         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20894             if(!this.calendarWeeks){
20895                 v.remove();
20896                 return;
20897             }
20898             
20899             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20900             v.attr('colspan', function(i, val){
20901                 return parseInt(val) + 1;
20902             });
20903         });
20904                         
20905         
20906         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20907         
20908         this.setStartDate(this.startDate);
20909         this.setEndDate(this.endDate);
20910         
20911         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20912         
20913         this.fillDow();
20914         this.fillMonths();
20915         this.update();
20916         this.showMode();
20917         
20918         if(this.isInline) {
20919             this.showPopup();
20920         }
20921     },
20922     
20923     picker : function()
20924     {
20925         return this.pickerEl;
20926 //        return this.el.select('.datepicker', true).first();
20927     },
20928     
20929     fillDow: function()
20930     {
20931         var dowCnt = this.weekStart;
20932         
20933         var dow = {
20934             tag: 'tr',
20935             cn: [
20936                 
20937             ]
20938         };
20939         
20940         if(this.calendarWeeks){
20941             dow.cn.push({
20942                 tag: 'th',
20943                 cls: 'cw',
20944                 html: '&nbsp;'
20945             })
20946         }
20947         
20948         while (dowCnt < this.weekStart + 7) {
20949             dow.cn.push({
20950                 tag: 'th',
20951                 cls: 'dow',
20952                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20953             });
20954         }
20955         
20956         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20957     },
20958     
20959     fillMonths: function()
20960     {    
20961         var i = 0;
20962         var months = this.picker().select('>.datepicker-months td', true).first();
20963         
20964         months.dom.innerHTML = '';
20965         
20966         while (i < 12) {
20967             var month = {
20968                 tag: 'span',
20969                 cls: 'month',
20970                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20971             };
20972             
20973             months.createChild(month);
20974         }
20975         
20976     },
20977     
20978     update: function()
20979     {
20980         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;
20981         
20982         if (this.date < this.startDate) {
20983             this.viewDate = new Date(this.startDate);
20984         } else if (this.date > this.endDate) {
20985             this.viewDate = new Date(this.endDate);
20986         } else {
20987             this.viewDate = new Date(this.date);
20988         }
20989         
20990         this.fill();
20991     },
20992     
20993     fill: function() 
20994     {
20995         var d = new Date(this.viewDate),
20996                 year = d.getUTCFullYear(),
20997                 month = d.getUTCMonth(),
20998                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20999                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21000                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21001                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21002                 currentDate = this.date && this.date.valueOf(),
21003                 today = this.UTCToday();
21004         
21005         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21006         
21007 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21008         
21009 //        this.picker.select('>tfoot th.today').
21010 //                                              .text(dates[this.language].today)
21011 //                                              .toggle(this.todayBtn !== false);
21012     
21013         this.updateNavArrows();
21014         this.fillMonths();
21015                                                 
21016         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21017         
21018         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21019          
21020         prevMonth.setUTCDate(day);
21021         
21022         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21023         
21024         var nextMonth = new Date(prevMonth);
21025         
21026         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21027         
21028         nextMonth = nextMonth.valueOf();
21029         
21030         var fillMonths = false;
21031         
21032         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21033         
21034         while(prevMonth.valueOf() <= nextMonth) {
21035             var clsName = '';
21036             
21037             if (prevMonth.getUTCDay() === this.weekStart) {
21038                 if(fillMonths){
21039                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21040                 }
21041                     
21042                 fillMonths = {
21043                     tag: 'tr',
21044                     cn: []
21045                 };
21046                 
21047                 if(this.calendarWeeks){
21048                     // ISO 8601: First week contains first thursday.
21049                     // ISO also states week starts on Monday, but we can be more abstract here.
21050                     var
21051                     // Start of current week: based on weekstart/current date
21052                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21053                     // Thursday of this week
21054                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21055                     // First Thursday of year, year from thursday
21056                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21057                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21058                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21059                     
21060                     fillMonths.cn.push({
21061                         tag: 'td',
21062                         cls: 'cw',
21063                         html: calWeek
21064                     });
21065                 }
21066             }
21067             
21068             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21069                 clsName += ' old';
21070             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21071                 clsName += ' new';
21072             }
21073             if (this.todayHighlight &&
21074                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21075                 prevMonth.getUTCMonth() == today.getMonth() &&
21076                 prevMonth.getUTCDate() == today.getDate()) {
21077                 clsName += ' today';
21078             }
21079             
21080             if (currentDate && prevMonth.valueOf() === currentDate) {
21081                 clsName += ' active';
21082             }
21083             
21084             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21085                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21086                     clsName += ' disabled';
21087             }
21088             
21089             fillMonths.cn.push({
21090                 tag: 'td',
21091                 cls: 'day ' + clsName,
21092                 html: prevMonth.getDate()
21093             });
21094             
21095             prevMonth.setDate(prevMonth.getDate()+1);
21096         }
21097           
21098         var currentYear = this.date && this.date.getUTCFullYear();
21099         var currentMonth = this.date && this.date.getUTCMonth();
21100         
21101         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21102         
21103         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21104             v.removeClass('active');
21105             
21106             if(currentYear === year && k === currentMonth){
21107                 v.addClass('active');
21108             }
21109             
21110             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21111                 v.addClass('disabled');
21112             }
21113             
21114         });
21115         
21116         
21117         year = parseInt(year/10, 10) * 10;
21118         
21119         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21120         
21121         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21122         
21123         year -= 1;
21124         for (var i = -1; i < 11; i++) {
21125             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21126                 tag: 'span',
21127                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21128                 html: year
21129             });
21130             
21131             year += 1;
21132         }
21133     },
21134     
21135     showMode: function(dir) 
21136     {
21137         if (dir) {
21138             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21139         }
21140         
21141         Roo.each(this.picker().select('>div',true).elements, function(v){
21142             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21143             v.hide();
21144         });
21145         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21146     },
21147     
21148     place: function()
21149     {
21150         if(this.isInline) {
21151             return;
21152         }
21153         
21154         this.picker().removeClass(['bottom', 'top']);
21155         
21156         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21157             /*
21158              * place to the top of element!
21159              *
21160              */
21161             
21162             this.picker().addClass('top');
21163             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21164             
21165             return;
21166         }
21167         
21168         this.picker().addClass('bottom');
21169         
21170         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21171     },
21172     
21173     parseDate : function(value)
21174     {
21175         if(!value || value instanceof Date){
21176             return value;
21177         }
21178         var v = Date.parseDate(value, this.format);
21179         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21180             v = Date.parseDate(value, 'Y-m-d');
21181         }
21182         if(!v && this.altFormats){
21183             if(!this.altFormatsArray){
21184                 this.altFormatsArray = this.altFormats.split("|");
21185             }
21186             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21187                 v = Date.parseDate(value, this.altFormatsArray[i]);
21188             }
21189         }
21190         return v;
21191     },
21192     
21193     formatDate : function(date, fmt)
21194     {   
21195         return (!date || !(date instanceof Date)) ?
21196         date : date.dateFormat(fmt || this.format);
21197     },
21198     
21199     onFocus : function()
21200     {
21201         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21202         this.showPopup();
21203     },
21204     
21205     onBlur : function()
21206     {
21207         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21208         
21209         var d = this.inputEl().getValue();
21210         
21211         this.setValue(d);
21212                 
21213         this.hidePopup();
21214     },
21215     
21216     showPopup : function()
21217     {
21218         this.picker().show();
21219         this.update();
21220         this.place();
21221         
21222         this.fireEvent('showpopup', this, this.date);
21223     },
21224     
21225     hidePopup : function()
21226     {
21227         if(this.isInline) {
21228             return;
21229         }
21230         this.picker().hide();
21231         this.viewMode = this.startViewMode;
21232         this.showMode();
21233         
21234         this.fireEvent('hidepopup', this, this.date);
21235         
21236     },
21237     
21238     onMousedown: function(e)
21239     {
21240         e.stopPropagation();
21241         e.preventDefault();
21242     },
21243     
21244     keyup: function(e)
21245     {
21246         Roo.bootstrap.DateField.superclass.keyup.call(this);
21247         this.update();
21248     },
21249
21250     setValue: function(v)
21251     {
21252         if(this.fireEvent('beforeselect', this, v) !== false){
21253             var d = new Date(this.parseDate(v) ).clearTime();
21254         
21255             if(isNaN(d.getTime())){
21256                 this.date = this.viewDate = '';
21257                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21258                 return;
21259             }
21260
21261             v = this.formatDate(d);
21262
21263             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21264
21265             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21266
21267             this.update();
21268
21269             this.fireEvent('select', this, this.date);
21270         }
21271     },
21272     
21273     getValue: function()
21274     {
21275         return this.formatDate(this.date);
21276     },
21277     
21278     fireKey: function(e)
21279     {
21280         if (!this.picker().isVisible()){
21281             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21282                 this.showPopup();
21283             }
21284             return;
21285         }
21286         
21287         var dateChanged = false,
21288         dir, day, month,
21289         newDate, newViewDate;
21290         
21291         switch(e.keyCode){
21292             case 27: // escape
21293                 this.hidePopup();
21294                 e.preventDefault();
21295                 break;
21296             case 37: // left
21297             case 39: // right
21298                 if (!this.keyboardNavigation) {
21299                     break;
21300                 }
21301                 dir = e.keyCode == 37 ? -1 : 1;
21302                 
21303                 if (e.ctrlKey){
21304                     newDate = this.moveYear(this.date, dir);
21305                     newViewDate = this.moveYear(this.viewDate, dir);
21306                 } else if (e.shiftKey){
21307                     newDate = this.moveMonth(this.date, dir);
21308                     newViewDate = this.moveMonth(this.viewDate, dir);
21309                 } else {
21310                     newDate = new Date(this.date);
21311                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21312                     newViewDate = new Date(this.viewDate);
21313                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21314                 }
21315                 if (this.dateWithinRange(newDate)){
21316                     this.date = newDate;
21317                     this.viewDate = newViewDate;
21318                     this.setValue(this.formatDate(this.date));
21319 //                    this.update();
21320                     e.preventDefault();
21321                     dateChanged = true;
21322                 }
21323                 break;
21324             case 38: // up
21325             case 40: // down
21326                 if (!this.keyboardNavigation) {
21327                     break;
21328                 }
21329                 dir = e.keyCode == 38 ? -1 : 1;
21330                 if (e.ctrlKey){
21331                     newDate = this.moveYear(this.date, dir);
21332                     newViewDate = this.moveYear(this.viewDate, dir);
21333                 } else if (e.shiftKey){
21334                     newDate = this.moveMonth(this.date, dir);
21335                     newViewDate = this.moveMonth(this.viewDate, dir);
21336                 } else {
21337                     newDate = new Date(this.date);
21338                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21339                     newViewDate = new Date(this.viewDate);
21340                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21341                 }
21342                 if (this.dateWithinRange(newDate)){
21343                     this.date = newDate;
21344                     this.viewDate = newViewDate;
21345                     this.setValue(this.formatDate(this.date));
21346 //                    this.update();
21347                     e.preventDefault();
21348                     dateChanged = true;
21349                 }
21350                 break;
21351             case 13: // enter
21352                 this.setValue(this.formatDate(this.date));
21353                 this.hidePopup();
21354                 e.preventDefault();
21355                 break;
21356             case 9: // tab
21357                 this.setValue(this.formatDate(this.date));
21358                 this.hidePopup();
21359                 break;
21360             case 16: // shift
21361             case 17: // ctrl
21362             case 18: // alt
21363                 break;
21364             default :
21365                 this.hidePopup();
21366                 
21367         }
21368     },
21369     
21370     
21371     onClick: function(e) 
21372     {
21373         e.stopPropagation();
21374         e.preventDefault();
21375         
21376         var target = e.getTarget();
21377         
21378         if(target.nodeName.toLowerCase() === 'i'){
21379             target = Roo.get(target).dom.parentNode;
21380         }
21381         
21382         var nodeName = target.nodeName;
21383         var className = target.className;
21384         var html = target.innerHTML;
21385         //Roo.log(nodeName);
21386         
21387         switch(nodeName.toLowerCase()) {
21388             case 'th':
21389                 switch(className) {
21390                     case 'switch':
21391                         this.showMode(1);
21392                         break;
21393                     case 'prev':
21394                     case 'next':
21395                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21396                         switch(this.viewMode){
21397                                 case 0:
21398                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21399                                         break;
21400                                 case 1:
21401                                 case 2:
21402                                         this.viewDate = this.moveYear(this.viewDate, dir);
21403                                         break;
21404                         }
21405                         this.fill();
21406                         break;
21407                     case 'today':
21408                         var date = new Date();
21409                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21410 //                        this.fill()
21411                         this.setValue(this.formatDate(this.date));
21412                         
21413                         this.hidePopup();
21414                         break;
21415                 }
21416                 break;
21417             case 'span':
21418                 if (className.indexOf('disabled') < 0) {
21419                     this.viewDate.setUTCDate(1);
21420                     if (className.indexOf('month') > -1) {
21421                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21422                     } else {
21423                         var year = parseInt(html, 10) || 0;
21424                         this.viewDate.setUTCFullYear(year);
21425                         
21426                     }
21427                     
21428                     if(this.singleMode){
21429                         this.setValue(this.formatDate(this.viewDate));
21430                         this.hidePopup();
21431                         return;
21432                     }
21433                     
21434                     this.showMode(-1);
21435                     this.fill();
21436                 }
21437                 break;
21438                 
21439             case 'td':
21440                 //Roo.log(className);
21441                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21442                     var day = parseInt(html, 10) || 1;
21443                     var year = this.viewDate.getUTCFullYear(),
21444                         month = this.viewDate.getUTCMonth();
21445
21446                     if (className.indexOf('old') > -1) {
21447                         if(month === 0 ){
21448                             month = 11;
21449                             year -= 1;
21450                         }else{
21451                             month -= 1;
21452                         }
21453                     } else if (className.indexOf('new') > -1) {
21454                         if (month == 11) {
21455                             month = 0;
21456                             year += 1;
21457                         } else {
21458                             month += 1;
21459                         }
21460                     }
21461                     //Roo.log([year,month,day]);
21462                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21463                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21464 //                    this.fill();
21465                     //Roo.log(this.formatDate(this.date));
21466                     this.setValue(this.formatDate(this.date));
21467                     this.hidePopup();
21468                 }
21469                 break;
21470         }
21471     },
21472     
21473     setStartDate: function(startDate)
21474     {
21475         this.startDate = startDate || -Infinity;
21476         if (this.startDate !== -Infinity) {
21477             this.startDate = this.parseDate(this.startDate);
21478         }
21479         this.update();
21480         this.updateNavArrows();
21481     },
21482
21483     setEndDate: function(endDate)
21484     {
21485         this.endDate = endDate || Infinity;
21486         if (this.endDate !== Infinity) {
21487             this.endDate = this.parseDate(this.endDate);
21488         }
21489         this.update();
21490         this.updateNavArrows();
21491     },
21492     
21493     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21494     {
21495         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21496         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21497             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21498         }
21499         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21500             return parseInt(d, 10);
21501         });
21502         this.update();
21503         this.updateNavArrows();
21504     },
21505     
21506     updateNavArrows: function() 
21507     {
21508         if(this.singleMode){
21509             return;
21510         }
21511         
21512         var d = new Date(this.viewDate),
21513         year = d.getUTCFullYear(),
21514         month = d.getUTCMonth();
21515         
21516         Roo.each(this.picker().select('.prev', true).elements, function(v){
21517             v.show();
21518             switch (this.viewMode) {
21519                 case 0:
21520
21521                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21522                         v.hide();
21523                     }
21524                     break;
21525                 case 1:
21526                 case 2:
21527                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21528                         v.hide();
21529                     }
21530                     break;
21531             }
21532         });
21533         
21534         Roo.each(this.picker().select('.next', true).elements, function(v){
21535             v.show();
21536             switch (this.viewMode) {
21537                 case 0:
21538
21539                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21540                         v.hide();
21541                     }
21542                     break;
21543                 case 1:
21544                 case 2:
21545                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21546                         v.hide();
21547                     }
21548                     break;
21549             }
21550         })
21551     },
21552     
21553     moveMonth: function(date, dir)
21554     {
21555         if (!dir) {
21556             return date;
21557         }
21558         var new_date = new Date(date.valueOf()),
21559         day = new_date.getUTCDate(),
21560         month = new_date.getUTCMonth(),
21561         mag = Math.abs(dir),
21562         new_month, test;
21563         dir = dir > 0 ? 1 : -1;
21564         if (mag == 1){
21565             test = dir == -1
21566             // If going back one month, make sure month is not current month
21567             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21568             ? function(){
21569                 return new_date.getUTCMonth() == month;
21570             }
21571             // If going forward one month, make sure month is as expected
21572             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21573             : function(){
21574                 return new_date.getUTCMonth() != new_month;
21575             };
21576             new_month = month + dir;
21577             new_date.setUTCMonth(new_month);
21578             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21579             if (new_month < 0 || new_month > 11) {
21580                 new_month = (new_month + 12) % 12;
21581             }
21582         } else {
21583             // For magnitudes >1, move one month at a time...
21584             for (var i=0; i<mag; i++) {
21585                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21586                 new_date = this.moveMonth(new_date, dir);
21587             }
21588             // ...then reset the day, keeping it in the new month
21589             new_month = new_date.getUTCMonth();
21590             new_date.setUTCDate(day);
21591             test = function(){
21592                 return new_month != new_date.getUTCMonth();
21593             };
21594         }
21595         // Common date-resetting loop -- if date is beyond end of month, make it
21596         // end of month
21597         while (test()){
21598             new_date.setUTCDate(--day);
21599             new_date.setUTCMonth(new_month);
21600         }
21601         return new_date;
21602     },
21603
21604     moveYear: function(date, dir)
21605     {
21606         return this.moveMonth(date, dir*12);
21607     },
21608
21609     dateWithinRange: function(date)
21610     {
21611         return date >= this.startDate && date <= this.endDate;
21612     },
21613
21614     
21615     remove: function() 
21616     {
21617         this.picker().remove();
21618     },
21619     
21620     validateValue : function(value)
21621     {
21622         if(this.getVisibilityEl().hasClass('hidden')){
21623             return true;
21624         }
21625         
21626         if(value.length < 1)  {
21627             if(this.allowBlank){
21628                 return true;
21629             }
21630             return false;
21631         }
21632         
21633         if(value.length < this.minLength){
21634             return false;
21635         }
21636         if(value.length > this.maxLength){
21637             return false;
21638         }
21639         if(this.vtype){
21640             var vt = Roo.form.VTypes;
21641             if(!vt[this.vtype](value, this)){
21642                 return false;
21643             }
21644         }
21645         if(typeof this.validator == "function"){
21646             var msg = this.validator(value);
21647             if(msg !== true){
21648                 return false;
21649             }
21650         }
21651         
21652         if(this.regex && !this.regex.test(value)){
21653             return false;
21654         }
21655         
21656         if(typeof(this.parseDate(value)) == 'undefined'){
21657             return false;
21658         }
21659         
21660         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21661             return false;
21662         }      
21663         
21664         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21665             return false;
21666         } 
21667         
21668         
21669         return true;
21670     },
21671     
21672     reset : function()
21673     {
21674         this.date = this.viewDate = '';
21675         
21676         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21677     }
21678    
21679 });
21680
21681 Roo.apply(Roo.bootstrap.DateField,  {
21682     
21683     head : {
21684         tag: 'thead',
21685         cn: [
21686         {
21687             tag: 'tr',
21688             cn: [
21689             {
21690                 tag: 'th',
21691                 cls: 'prev',
21692                 html: '<i class="fa fa-arrow-left"/>'
21693             },
21694             {
21695                 tag: 'th',
21696                 cls: 'switch',
21697                 colspan: '5'
21698             },
21699             {
21700                 tag: 'th',
21701                 cls: 'next',
21702                 html: '<i class="fa fa-arrow-right"/>'
21703             }
21704
21705             ]
21706         }
21707         ]
21708     },
21709     
21710     content : {
21711         tag: 'tbody',
21712         cn: [
21713         {
21714             tag: 'tr',
21715             cn: [
21716             {
21717                 tag: 'td',
21718                 colspan: '7'
21719             }
21720             ]
21721         }
21722         ]
21723     },
21724     
21725     footer : {
21726         tag: 'tfoot',
21727         cn: [
21728         {
21729             tag: 'tr',
21730             cn: [
21731             {
21732                 tag: 'th',
21733                 colspan: '7',
21734                 cls: 'today'
21735             }
21736                     
21737             ]
21738         }
21739         ]
21740     },
21741     
21742     dates:{
21743         en: {
21744             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21745             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21746             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21747             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21748             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21749             today: "Today"
21750         }
21751     },
21752     
21753     modes: [
21754     {
21755         clsName: 'days',
21756         navFnc: 'Month',
21757         navStep: 1
21758     },
21759     {
21760         clsName: 'months',
21761         navFnc: 'FullYear',
21762         navStep: 1
21763     },
21764     {
21765         clsName: 'years',
21766         navFnc: 'FullYear',
21767         navStep: 10
21768     }]
21769 });
21770
21771 Roo.apply(Roo.bootstrap.DateField,  {
21772   
21773     template : {
21774         tag: 'div',
21775         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21776         cn: [
21777         {
21778             tag: 'div',
21779             cls: 'datepicker-days',
21780             cn: [
21781             {
21782                 tag: 'table',
21783                 cls: 'table-condensed',
21784                 cn:[
21785                 Roo.bootstrap.DateField.head,
21786                 {
21787                     tag: 'tbody'
21788                 },
21789                 Roo.bootstrap.DateField.footer
21790                 ]
21791             }
21792             ]
21793         },
21794         {
21795             tag: 'div',
21796             cls: 'datepicker-months',
21797             cn: [
21798             {
21799                 tag: 'table',
21800                 cls: 'table-condensed',
21801                 cn:[
21802                 Roo.bootstrap.DateField.head,
21803                 Roo.bootstrap.DateField.content,
21804                 Roo.bootstrap.DateField.footer
21805                 ]
21806             }
21807             ]
21808         },
21809         {
21810             tag: 'div',
21811             cls: 'datepicker-years',
21812             cn: [
21813             {
21814                 tag: 'table',
21815                 cls: 'table-condensed',
21816                 cn:[
21817                 Roo.bootstrap.DateField.head,
21818                 Roo.bootstrap.DateField.content,
21819                 Roo.bootstrap.DateField.footer
21820                 ]
21821             }
21822             ]
21823         }
21824         ]
21825     }
21826 });
21827
21828  
21829
21830  /*
21831  * - LGPL
21832  *
21833  * TimeField
21834  * 
21835  */
21836
21837 /**
21838  * @class Roo.bootstrap.TimeField
21839  * @extends Roo.bootstrap.Input
21840  * Bootstrap DateField class
21841  * 
21842  * 
21843  * @constructor
21844  * Create a new TimeField
21845  * @param {Object} config The config object
21846  */
21847
21848 Roo.bootstrap.TimeField = function(config){
21849     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21850     this.addEvents({
21851             /**
21852              * @event show
21853              * Fires when this field show.
21854              * @param {Roo.bootstrap.DateField} thisthis
21855              * @param {Mixed} date The date value
21856              */
21857             show : true,
21858             /**
21859              * @event show
21860              * Fires when this field hide.
21861              * @param {Roo.bootstrap.DateField} this
21862              * @param {Mixed} date The date value
21863              */
21864             hide : true,
21865             /**
21866              * @event select
21867              * Fires when select a date.
21868              * @param {Roo.bootstrap.DateField} this
21869              * @param {Mixed} date The date value
21870              */
21871             select : true
21872         });
21873 };
21874
21875 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21876     
21877     /**
21878      * @cfg {String} format
21879      * The default time format string which can be overriden for localization support.  The format must be
21880      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21881      */
21882     format : "H:i",
21883        
21884     onRender: function(ct, position)
21885     {
21886         
21887         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21888                 
21889         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21890         
21891         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21892         
21893         this.pop = this.picker().select('>.datepicker-time',true).first();
21894         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21895         
21896         this.picker().on('mousedown', this.onMousedown, this);
21897         this.picker().on('click', this.onClick, this);
21898         
21899         this.picker().addClass('datepicker-dropdown');
21900     
21901         this.fillTime();
21902         this.update();
21903             
21904         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21905         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21906         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21907         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21908         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21909         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21910
21911     },
21912     
21913     fireKey: function(e){
21914         if (!this.picker().isVisible()){
21915             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21916                 this.show();
21917             }
21918             return;
21919         }
21920
21921         e.preventDefault();
21922         
21923         switch(e.keyCode){
21924             case 27: // escape
21925                 this.hide();
21926                 break;
21927             case 37: // left
21928             case 39: // right
21929                 this.onTogglePeriod();
21930                 break;
21931             case 38: // up
21932                 this.onIncrementMinutes();
21933                 break;
21934             case 40: // down
21935                 this.onDecrementMinutes();
21936                 break;
21937             case 13: // enter
21938             case 9: // tab
21939                 this.setTime();
21940                 break;
21941         }
21942     },
21943     
21944     onClick: function(e) {
21945         e.stopPropagation();
21946         e.preventDefault();
21947     },
21948     
21949     picker : function()
21950     {
21951         return this.el.select('.datepicker', true).first();
21952     },
21953     
21954     fillTime: function()
21955     {    
21956         var time = this.pop.select('tbody', true).first();
21957         
21958         time.dom.innerHTML = '';
21959         
21960         time.createChild({
21961             tag: 'tr',
21962             cn: [
21963                 {
21964                     tag: 'td',
21965                     cn: [
21966                         {
21967                             tag: 'a',
21968                             href: '#',
21969                             cls: 'btn',
21970                             cn: [
21971                                 {
21972                                     tag: 'span',
21973                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21974                                 }
21975                             ]
21976                         } 
21977                     ]
21978                 },
21979                 {
21980                     tag: 'td',
21981                     cls: 'separator'
21982                 },
21983                 {
21984                     tag: 'td',
21985                     cn: [
21986                         {
21987                             tag: 'a',
21988                             href: '#',
21989                             cls: 'btn',
21990                             cn: [
21991                                 {
21992                                     tag: 'span',
21993                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21994                                 }
21995                             ]
21996                         }
21997                     ]
21998                 },
21999                 {
22000                     tag: 'td',
22001                     cls: 'separator'
22002                 }
22003             ]
22004         });
22005         
22006         time.createChild({
22007             tag: 'tr',
22008             cn: [
22009                 {
22010                     tag: 'td',
22011                     cn: [
22012                         {
22013                             tag: 'span',
22014                             cls: 'timepicker-hour',
22015                             html: '00'
22016                         }  
22017                     ]
22018                 },
22019                 {
22020                     tag: 'td',
22021                     cls: 'separator',
22022                     html: ':'
22023                 },
22024                 {
22025                     tag: 'td',
22026                     cn: [
22027                         {
22028                             tag: 'span',
22029                             cls: 'timepicker-minute',
22030                             html: '00'
22031                         }  
22032                     ]
22033                 },
22034                 {
22035                     tag: 'td',
22036                     cls: 'separator'
22037                 },
22038                 {
22039                     tag: 'td',
22040                     cn: [
22041                         {
22042                             tag: 'button',
22043                             type: 'button',
22044                             cls: 'btn btn-primary period',
22045                             html: 'AM'
22046                             
22047                         }
22048                     ]
22049                 }
22050             ]
22051         });
22052         
22053         time.createChild({
22054             tag: 'tr',
22055             cn: [
22056                 {
22057                     tag: 'td',
22058                     cn: [
22059                         {
22060                             tag: 'a',
22061                             href: '#',
22062                             cls: 'btn',
22063                             cn: [
22064                                 {
22065                                     tag: 'span',
22066                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22067                                 }
22068                             ]
22069                         }
22070                     ]
22071                 },
22072                 {
22073                     tag: 'td',
22074                     cls: 'separator'
22075                 },
22076                 {
22077                     tag: 'td',
22078                     cn: [
22079                         {
22080                             tag: 'a',
22081                             href: '#',
22082                             cls: 'btn',
22083                             cn: [
22084                                 {
22085                                     tag: 'span',
22086                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22087                                 }
22088                             ]
22089                         }
22090                     ]
22091                 },
22092                 {
22093                     tag: 'td',
22094                     cls: 'separator'
22095                 }
22096             ]
22097         });
22098         
22099     },
22100     
22101     update: function()
22102     {
22103         
22104         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22105         
22106         this.fill();
22107     },
22108     
22109     fill: function() 
22110     {
22111         var hours = this.time.getHours();
22112         var minutes = this.time.getMinutes();
22113         var period = 'AM';
22114         
22115         if(hours > 11){
22116             period = 'PM';
22117         }
22118         
22119         if(hours == 0){
22120             hours = 12;
22121         }
22122         
22123         
22124         if(hours > 12){
22125             hours = hours - 12;
22126         }
22127         
22128         if(hours < 10){
22129             hours = '0' + hours;
22130         }
22131         
22132         if(minutes < 10){
22133             minutes = '0' + minutes;
22134         }
22135         
22136         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22137         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22138         this.pop.select('button', true).first().dom.innerHTML = period;
22139         
22140     },
22141     
22142     place: function()
22143     {   
22144         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22145         
22146         var cls = ['bottom'];
22147         
22148         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22149             cls.pop();
22150             cls.push('top');
22151         }
22152         
22153         cls.push('right');
22154         
22155         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22156             cls.pop();
22157             cls.push('left');
22158         }
22159         
22160         this.picker().addClass(cls.join('-'));
22161         
22162         var _this = this;
22163         
22164         Roo.each(cls, function(c){
22165             if(c == 'bottom'){
22166                 _this.picker().setTop(_this.inputEl().getHeight());
22167                 return;
22168             }
22169             if(c == 'top'){
22170                 _this.picker().setTop(0 - _this.picker().getHeight());
22171                 return;
22172             }
22173             
22174             if(c == 'left'){
22175                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22176                 return;
22177             }
22178             if(c == 'right'){
22179                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22180                 return;
22181             }
22182         });
22183         
22184     },
22185   
22186     onFocus : function()
22187     {
22188         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22189         this.show();
22190     },
22191     
22192     onBlur : function()
22193     {
22194         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22195         this.hide();
22196     },
22197     
22198     show : function()
22199     {
22200         this.picker().show();
22201         this.pop.show();
22202         this.update();
22203         this.place();
22204         
22205         this.fireEvent('show', this, this.date);
22206     },
22207     
22208     hide : function()
22209     {
22210         this.picker().hide();
22211         this.pop.hide();
22212         
22213         this.fireEvent('hide', this, this.date);
22214     },
22215     
22216     setTime : function()
22217     {
22218         this.hide();
22219         this.setValue(this.time.format(this.format));
22220         
22221         this.fireEvent('select', this, this.date);
22222         
22223         
22224     },
22225     
22226     onMousedown: function(e){
22227         e.stopPropagation();
22228         e.preventDefault();
22229     },
22230     
22231     onIncrementHours: function()
22232     {
22233         Roo.log('onIncrementHours');
22234         this.time = this.time.add(Date.HOUR, 1);
22235         this.update();
22236         
22237     },
22238     
22239     onDecrementHours: function()
22240     {
22241         Roo.log('onDecrementHours');
22242         this.time = this.time.add(Date.HOUR, -1);
22243         this.update();
22244     },
22245     
22246     onIncrementMinutes: function()
22247     {
22248         Roo.log('onIncrementMinutes');
22249         this.time = this.time.add(Date.MINUTE, 1);
22250         this.update();
22251     },
22252     
22253     onDecrementMinutes: function()
22254     {
22255         Roo.log('onDecrementMinutes');
22256         this.time = this.time.add(Date.MINUTE, -1);
22257         this.update();
22258     },
22259     
22260     onTogglePeriod: function()
22261     {
22262         Roo.log('onTogglePeriod');
22263         this.time = this.time.add(Date.HOUR, 12);
22264         this.update();
22265     }
22266     
22267    
22268 });
22269
22270 Roo.apply(Roo.bootstrap.TimeField,  {
22271     
22272     content : {
22273         tag: 'tbody',
22274         cn: [
22275             {
22276                 tag: 'tr',
22277                 cn: [
22278                 {
22279                     tag: 'td',
22280                     colspan: '7'
22281                 }
22282                 ]
22283             }
22284         ]
22285     },
22286     
22287     footer : {
22288         tag: 'tfoot',
22289         cn: [
22290             {
22291                 tag: 'tr',
22292                 cn: [
22293                 {
22294                     tag: 'th',
22295                     colspan: '7',
22296                     cls: '',
22297                     cn: [
22298                         {
22299                             tag: 'button',
22300                             cls: 'btn btn-info ok',
22301                             html: 'OK'
22302                         }
22303                     ]
22304                 }
22305
22306                 ]
22307             }
22308         ]
22309     }
22310 });
22311
22312 Roo.apply(Roo.bootstrap.TimeField,  {
22313   
22314     template : {
22315         tag: 'div',
22316         cls: 'datepicker dropdown-menu',
22317         cn: [
22318             {
22319                 tag: 'div',
22320                 cls: 'datepicker-time',
22321                 cn: [
22322                 {
22323                     tag: 'table',
22324                     cls: 'table-condensed',
22325                     cn:[
22326                     Roo.bootstrap.TimeField.content,
22327                     Roo.bootstrap.TimeField.footer
22328                     ]
22329                 }
22330                 ]
22331             }
22332         ]
22333     }
22334 });
22335
22336  
22337
22338  /*
22339  * - LGPL
22340  *
22341  * MonthField
22342  * 
22343  */
22344
22345 /**
22346  * @class Roo.bootstrap.MonthField
22347  * @extends Roo.bootstrap.Input
22348  * Bootstrap MonthField class
22349  * 
22350  * @cfg {String} language default en
22351  * 
22352  * @constructor
22353  * Create a new MonthField
22354  * @param {Object} config The config object
22355  */
22356
22357 Roo.bootstrap.MonthField = function(config){
22358     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22359     
22360     this.addEvents({
22361         /**
22362          * @event show
22363          * Fires when this field show.
22364          * @param {Roo.bootstrap.MonthField} this
22365          * @param {Mixed} date The date value
22366          */
22367         show : true,
22368         /**
22369          * @event show
22370          * Fires when this field hide.
22371          * @param {Roo.bootstrap.MonthField} this
22372          * @param {Mixed} date The date value
22373          */
22374         hide : true,
22375         /**
22376          * @event select
22377          * Fires when select a date.
22378          * @param {Roo.bootstrap.MonthField} this
22379          * @param {String} oldvalue The old value
22380          * @param {String} newvalue The new value
22381          */
22382         select : true
22383     });
22384 };
22385
22386 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22387     
22388     onRender: function(ct, position)
22389     {
22390         
22391         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22392         
22393         this.language = this.language || 'en';
22394         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22395         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22396         
22397         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22398         this.isInline = false;
22399         this.isInput = true;
22400         this.component = this.el.select('.add-on', true).first() || false;
22401         this.component = (this.component && this.component.length === 0) ? false : this.component;
22402         this.hasInput = this.component && this.inputEL().length;
22403         
22404         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22405         
22406         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22407         
22408         this.picker().on('mousedown', this.onMousedown, this);
22409         this.picker().on('click', this.onClick, this);
22410         
22411         this.picker().addClass('datepicker-dropdown');
22412         
22413         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22414             v.setStyle('width', '189px');
22415         });
22416         
22417         this.fillMonths();
22418         
22419         this.update();
22420         
22421         if(this.isInline) {
22422             this.show();
22423         }
22424         
22425     },
22426     
22427     setValue: function(v, suppressEvent)
22428     {   
22429         var o = this.getValue();
22430         
22431         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22432         
22433         this.update();
22434
22435         if(suppressEvent !== true){
22436             this.fireEvent('select', this, o, v);
22437         }
22438         
22439     },
22440     
22441     getValue: function()
22442     {
22443         return this.value;
22444     },
22445     
22446     onClick: function(e) 
22447     {
22448         e.stopPropagation();
22449         e.preventDefault();
22450         
22451         var target = e.getTarget();
22452         
22453         if(target.nodeName.toLowerCase() === 'i'){
22454             target = Roo.get(target).dom.parentNode;
22455         }
22456         
22457         var nodeName = target.nodeName;
22458         var className = target.className;
22459         var html = target.innerHTML;
22460         
22461         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22462             return;
22463         }
22464         
22465         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22466         
22467         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22468         
22469         this.hide();
22470                         
22471     },
22472     
22473     picker : function()
22474     {
22475         return this.pickerEl;
22476     },
22477     
22478     fillMonths: function()
22479     {    
22480         var i = 0;
22481         var months = this.picker().select('>.datepicker-months td', true).first();
22482         
22483         months.dom.innerHTML = '';
22484         
22485         while (i < 12) {
22486             var month = {
22487                 tag: 'span',
22488                 cls: 'month',
22489                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22490             };
22491             
22492             months.createChild(month);
22493         }
22494         
22495     },
22496     
22497     update: function()
22498     {
22499         var _this = this;
22500         
22501         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22502             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22503         }
22504         
22505         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22506             e.removeClass('active');
22507             
22508             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22509                 e.addClass('active');
22510             }
22511         })
22512     },
22513     
22514     place: function()
22515     {
22516         if(this.isInline) {
22517             return;
22518         }
22519         
22520         this.picker().removeClass(['bottom', 'top']);
22521         
22522         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22523             /*
22524              * place to the top of element!
22525              *
22526              */
22527             
22528             this.picker().addClass('top');
22529             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22530             
22531             return;
22532         }
22533         
22534         this.picker().addClass('bottom');
22535         
22536         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22537     },
22538     
22539     onFocus : function()
22540     {
22541         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22542         this.show();
22543     },
22544     
22545     onBlur : function()
22546     {
22547         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22548         
22549         var d = this.inputEl().getValue();
22550         
22551         this.setValue(d);
22552                 
22553         this.hide();
22554     },
22555     
22556     show : function()
22557     {
22558         this.picker().show();
22559         this.picker().select('>.datepicker-months', true).first().show();
22560         this.update();
22561         this.place();
22562         
22563         this.fireEvent('show', this, this.date);
22564     },
22565     
22566     hide : function()
22567     {
22568         if(this.isInline) {
22569             return;
22570         }
22571         this.picker().hide();
22572         this.fireEvent('hide', this, this.date);
22573         
22574     },
22575     
22576     onMousedown: function(e)
22577     {
22578         e.stopPropagation();
22579         e.preventDefault();
22580     },
22581     
22582     keyup: function(e)
22583     {
22584         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22585         this.update();
22586     },
22587
22588     fireKey: function(e)
22589     {
22590         if (!this.picker().isVisible()){
22591             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22592                 this.show();
22593             }
22594             return;
22595         }
22596         
22597         var dir;
22598         
22599         switch(e.keyCode){
22600             case 27: // escape
22601                 this.hide();
22602                 e.preventDefault();
22603                 break;
22604             case 37: // left
22605             case 39: // right
22606                 dir = e.keyCode == 37 ? -1 : 1;
22607                 
22608                 this.vIndex = this.vIndex + dir;
22609                 
22610                 if(this.vIndex < 0){
22611                     this.vIndex = 0;
22612                 }
22613                 
22614                 if(this.vIndex > 11){
22615                     this.vIndex = 11;
22616                 }
22617                 
22618                 if(isNaN(this.vIndex)){
22619                     this.vIndex = 0;
22620                 }
22621                 
22622                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22623                 
22624                 break;
22625             case 38: // up
22626             case 40: // down
22627                 
22628                 dir = e.keyCode == 38 ? -1 : 1;
22629                 
22630                 this.vIndex = this.vIndex + dir * 4;
22631                 
22632                 if(this.vIndex < 0){
22633                     this.vIndex = 0;
22634                 }
22635                 
22636                 if(this.vIndex > 11){
22637                     this.vIndex = 11;
22638                 }
22639                 
22640                 if(isNaN(this.vIndex)){
22641                     this.vIndex = 0;
22642                 }
22643                 
22644                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22645                 break;
22646                 
22647             case 13: // enter
22648                 
22649                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22650                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22651                 }
22652                 
22653                 this.hide();
22654                 e.preventDefault();
22655                 break;
22656             case 9: // tab
22657                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22658                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22659                 }
22660                 this.hide();
22661                 break;
22662             case 16: // shift
22663             case 17: // ctrl
22664             case 18: // alt
22665                 break;
22666             default :
22667                 this.hide();
22668                 
22669         }
22670     },
22671     
22672     remove: function() 
22673     {
22674         this.picker().remove();
22675     }
22676    
22677 });
22678
22679 Roo.apply(Roo.bootstrap.MonthField,  {
22680     
22681     content : {
22682         tag: 'tbody',
22683         cn: [
22684         {
22685             tag: 'tr',
22686             cn: [
22687             {
22688                 tag: 'td',
22689                 colspan: '7'
22690             }
22691             ]
22692         }
22693         ]
22694     },
22695     
22696     dates:{
22697         en: {
22698             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22699             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22700         }
22701     }
22702 });
22703
22704 Roo.apply(Roo.bootstrap.MonthField,  {
22705   
22706     template : {
22707         tag: 'div',
22708         cls: 'datepicker dropdown-menu roo-dynamic',
22709         cn: [
22710             {
22711                 tag: 'div',
22712                 cls: 'datepicker-months',
22713                 cn: [
22714                 {
22715                     tag: 'table',
22716                     cls: 'table-condensed',
22717                     cn:[
22718                         Roo.bootstrap.DateField.content
22719                     ]
22720                 }
22721                 ]
22722             }
22723         ]
22724     }
22725 });
22726
22727  
22728
22729  
22730  /*
22731  * - LGPL
22732  *
22733  * CheckBox
22734  * 
22735  */
22736
22737 /**
22738  * @class Roo.bootstrap.CheckBox
22739  * @extends Roo.bootstrap.Input
22740  * Bootstrap CheckBox class
22741  * 
22742  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22743  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22744  * @cfg {String} boxLabel The text that appears beside the checkbox
22745  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22746  * @cfg {Boolean} checked initnal the element
22747  * @cfg {Boolean} inline inline the element (default false)
22748  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22749  * @cfg {String} tooltip label tooltip
22750  * 
22751  * @constructor
22752  * Create a new CheckBox
22753  * @param {Object} config The config object
22754  */
22755
22756 Roo.bootstrap.CheckBox = function(config){
22757     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22758    
22759     this.addEvents({
22760         /**
22761         * @event check
22762         * Fires when the element is checked or unchecked.
22763         * @param {Roo.bootstrap.CheckBox} this This input
22764         * @param {Boolean} checked The new checked value
22765         */
22766        check : true,
22767        /**
22768         * @event click
22769         * Fires when the element is click.
22770         * @param {Roo.bootstrap.CheckBox} this This input
22771         */
22772        click : true
22773     });
22774     
22775 };
22776
22777 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22778   
22779     inputType: 'checkbox',
22780     inputValue: 1,
22781     valueOff: 0,
22782     boxLabel: false,
22783     checked: false,
22784     weight : false,
22785     inline: false,
22786     tooltip : '',
22787     
22788     // checkbox success does not make any sense really.. 
22789     invalidClass : "",
22790     validClass : "",
22791     
22792     
22793     getAutoCreate : function()
22794     {
22795         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22796         
22797         var id = Roo.id();
22798         
22799         var cfg = {};
22800         
22801         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22802         
22803         if(this.inline){
22804             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22805         }
22806         
22807         var input =  {
22808             tag: 'input',
22809             id : id,
22810             type : this.inputType,
22811             value : this.inputValue,
22812             cls : 'roo-' + this.inputType, //'form-box',
22813             placeholder : this.placeholder || ''
22814             
22815         };
22816         
22817         if(this.inputType != 'radio'){
22818             var hidden =  {
22819                 tag: 'input',
22820                 type : 'hidden',
22821                 cls : 'roo-hidden-value',
22822                 value : this.checked ? this.inputValue : this.valueOff
22823             };
22824         }
22825         
22826             
22827         if (this.weight) { // Validity check?
22828             cfg.cls += " " + this.inputType + "-" + this.weight;
22829         }
22830         
22831         if (this.disabled) {
22832             input.disabled=true;
22833         }
22834         
22835         if(this.checked){
22836             input.checked = this.checked;
22837         }
22838         
22839         if (this.name) {
22840             
22841             input.name = this.name;
22842             
22843             if(this.inputType != 'radio'){
22844                 hidden.name = this.name;
22845                 input.name = '_hidden_' + this.name;
22846             }
22847         }
22848         
22849         if (this.size) {
22850             input.cls += ' input-' + this.size;
22851         }
22852         
22853         var settings=this;
22854         
22855         ['xs','sm','md','lg'].map(function(size){
22856             if (settings[size]) {
22857                 cfg.cls += ' col-' + size + '-' + settings[size];
22858             }
22859         });
22860         
22861         var inputblock = input;
22862          
22863         if (this.before || this.after) {
22864             
22865             inputblock = {
22866                 cls : 'input-group',
22867                 cn :  [] 
22868             };
22869             
22870             if (this.before) {
22871                 inputblock.cn.push({
22872                     tag :'span',
22873                     cls : 'input-group-addon',
22874                     html : this.before
22875                 });
22876             }
22877             
22878             inputblock.cn.push(input);
22879             
22880             if(this.inputType != 'radio'){
22881                 inputblock.cn.push(hidden);
22882             }
22883             
22884             if (this.after) {
22885                 inputblock.cn.push({
22886                     tag :'span',
22887                     cls : 'input-group-addon',
22888                     html : this.after
22889                 });
22890             }
22891             
22892         }
22893         var boxLabelCfg = false;
22894         
22895         if(this.boxLabel){
22896            
22897             boxLabelCfg = {
22898                 tag: 'label',
22899                 //'for': id, // box label is handled by onclick - so no for...
22900                 cls: 'box-label',
22901                 html: this.boxLabel
22902             };
22903             if(this.tooltip){
22904                 boxLabelCfg.tooltip = this.tooltip;
22905             }
22906              
22907         }
22908         
22909         
22910         if (align ==='left' && this.fieldLabel.length) {
22911 //                Roo.log("left and has label");
22912             cfg.cn = [
22913                 {
22914                     tag: 'label',
22915                     'for' :  id,
22916                     cls : 'control-label',
22917                     html : this.fieldLabel
22918                 },
22919                 {
22920                     cls : "", 
22921                     cn: [
22922                         inputblock
22923                     ]
22924                 }
22925             ];
22926             
22927             if (boxLabelCfg) {
22928                 cfg.cn[1].cn.push(boxLabelCfg);
22929             }
22930             
22931             if(this.labelWidth > 12){
22932                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22933             }
22934             
22935             if(this.labelWidth < 13 && this.labelmd == 0){
22936                 this.labelmd = this.labelWidth;
22937             }
22938             
22939             if(this.labellg > 0){
22940                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22941                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22942             }
22943             
22944             if(this.labelmd > 0){
22945                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22946                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22947             }
22948             
22949             if(this.labelsm > 0){
22950                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22951                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22952             }
22953             
22954             if(this.labelxs > 0){
22955                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22956                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22957             }
22958             
22959         } else if ( this.fieldLabel.length) {
22960 //                Roo.log(" label");
22961                 cfg.cn = [
22962                    
22963                     {
22964                         tag: this.boxLabel ? 'span' : 'label',
22965                         'for': id,
22966                         cls: 'control-label box-input-label',
22967                         //cls : 'input-group-addon',
22968                         html : this.fieldLabel
22969                     },
22970                     
22971                     inputblock
22972                     
22973                 ];
22974                 if (boxLabelCfg) {
22975                     cfg.cn.push(boxLabelCfg);
22976                 }
22977
22978         } else {
22979             
22980 //                Roo.log(" no label && no align");
22981                 cfg.cn = [  inputblock ] ;
22982                 if (boxLabelCfg) {
22983                     cfg.cn.push(boxLabelCfg);
22984                 }
22985
22986                 
22987         }
22988         
22989        
22990         
22991         if(this.inputType != 'radio'){
22992             cfg.cn.push(hidden);
22993         }
22994         
22995         return cfg;
22996         
22997     },
22998     
22999     /**
23000      * return the real input element.
23001      */
23002     inputEl: function ()
23003     {
23004         return this.el.select('input.roo-' + this.inputType,true).first();
23005     },
23006     hiddenEl: function ()
23007     {
23008         return this.el.select('input.roo-hidden-value',true).first();
23009     },
23010     
23011     labelEl: function()
23012     {
23013         return this.el.select('label.control-label',true).first();
23014     },
23015     /* depricated... */
23016     
23017     label: function()
23018     {
23019         return this.labelEl();
23020     },
23021     
23022     boxLabelEl: function()
23023     {
23024         return this.el.select('label.box-label',true).first();
23025     },
23026     
23027     initEvents : function()
23028     {
23029 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23030         
23031         this.inputEl().on('click', this.onClick,  this);
23032         
23033         if (this.boxLabel) { 
23034             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23035         }
23036         
23037         this.startValue = this.getValue();
23038         
23039         if(this.groupId){
23040             Roo.bootstrap.CheckBox.register(this);
23041         }
23042     },
23043     
23044     onClick : function(e)
23045     {   
23046         if(this.fireEvent('click', this, e) !== false){
23047             this.setChecked(!this.checked);
23048         }
23049         
23050     },
23051     
23052     setChecked : function(state,suppressEvent)
23053     {
23054         this.startValue = this.getValue();
23055
23056         if(this.inputType == 'radio'){
23057             
23058             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23059                 e.dom.checked = false;
23060             });
23061             
23062             this.inputEl().dom.checked = true;
23063             
23064             this.inputEl().dom.value = this.inputValue;
23065             
23066             if(suppressEvent !== true){
23067                 this.fireEvent('check', this, true);
23068             }
23069             
23070             this.validate();
23071             
23072             return;
23073         }
23074         
23075         this.checked = state;
23076         
23077         this.inputEl().dom.checked = state;
23078         
23079         
23080         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23081         
23082         if(suppressEvent !== true){
23083             this.fireEvent('check', this, state);
23084         }
23085         
23086         this.validate();
23087     },
23088     
23089     getValue : function()
23090     {
23091         if(this.inputType == 'radio'){
23092             return this.getGroupValue();
23093         }
23094         
23095         return this.hiddenEl().dom.value;
23096         
23097     },
23098     
23099     getGroupValue : function()
23100     {
23101         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23102             return '';
23103         }
23104         
23105         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23106     },
23107     
23108     setValue : function(v,suppressEvent)
23109     {
23110         if(this.inputType == 'radio'){
23111             this.setGroupValue(v, suppressEvent);
23112             return;
23113         }
23114         
23115         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23116         
23117         this.validate();
23118     },
23119     
23120     setGroupValue : function(v, suppressEvent)
23121     {
23122         this.startValue = this.getValue();
23123         
23124         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23125             e.dom.checked = false;
23126             
23127             if(e.dom.value == v){
23128                 e.dom.checked = true;
23129             }
23130         });
23131         
23132         if(suppressEvent !== true){
23133             this.fireEvent('check', this, true);
23134         }
23135
23136         this.validate();
23137         
23138         return;
23139     },
23140     
23141     validate : function()
23142     {
23143         if(this.getVisibilityEl().hasClass('hidden')){
23144             return true;
23145         }
23146         
23147         if(
23148                 this.disabled || 
23149                 (this.inputType == 'radio' && this.validateRadio()) ||
23150                 (this.inputType == 'checkbox' && this.validateCheckbox())
23151         ){
23152             this.markValid();
23153             return true;
23154         }
23155         
23156         this.markInvalid();
23157         return false;
23158     },
23159     
23160     validateRadio : function()
23161     {
23162         if(this.getVisibilityEl().hasClass('hidden')){
23163             return true;
23164         }
23165         
23166         if(this.allowBlank){
23167             return true;
23168         }
23169         
23170         var valid = false;
23171         
23172         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23173             if(!e.dom.checked){
23174                 return;
23175             }
23176             
23177             valid = true;
23178             
23179             return false;
23180         });
23181         
23182         return valid;
23183     },
23184     
23185     validateCheckbox : function()
23186     {
23187         if(!this.groupId){
23188             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23189             //return (this.getValue() == this.inputValue) ? true : false;
23190         }
23191         
23192         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23193         
23194         if(!group){
23195             return false;
23196         }
23197         
23198         var r = false;
23199         
23200         for(var i in group){
23201             if(group[i].el.isVisible(true)){
23202                 r = false;
23203                 break;
23204             }
23205             
23206             r = true;
23207         }
23208         
23209         for(var i in group){
23210             if(r){
23211                 break;
23212             }
23213             
23214             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23215         }
23216         
23217         return r;
23218     },
23219     
23220     /**
23221      * Mark this field as valid
23222      */
23223     markValid : function()
23224     {
23225         var _this = this;
23226         
23227         this.fireEvent('valid', this);
23228         
23229         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23230         
23231         if(this.groupId){
23232             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23233         }
23234         
23235         if(label){
23236             label.markValid();
23237         }
23238
23239         if(this.inputType == 'radio'){
23240             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23241                 var fg = e.findParent('.form-group', false, true);
23242                 if (Roo.bootstrap.version == 3) {
23243                     fg.removeClass([_this.invalidClass, _this.validClass]);
23244                     fg.addClass(_this.validClass);
23245                 } else {
23246                     fg.removeClass(['is-valid', 'is-invalid']);
23247                     fg.addClass('is-valid');
23248                 }
23249             });
23250             
23251             return;
23252         }
23253
23254         if(!this.groupId){
23255             var fg = this.el.findParent('.form-group', false, true);
23256             if (Roo.bootstrap.version == 3) {
23257                 fg.removeClass([this.invalidClass, this.validClass]);
23258                 fg.addClass(this.validClass);
23259             } else {
23260                 fg.removeClass(['is-valid', 'is-invalid']);
23261                 fg.addClass('is-valid');
23262             }
23263             return;
23264         }
23265         
23266         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23267         
23268         if(!group){
23269             return;
23270         }
23271         
23272         for(var i in group){
23273             var fg = group[i].el.findParent('.form-group', false, true);
23274             if (Roo.bootstrap.version == 3) {
23275                 fg.removeClass([this.invalidClass, this.validClass]);
23276                 fg.addClass(this.validClass);
23277             } else {
23278                 fg.removeClass(['is-valid', 'is-invalid']);
23279                 fg.addClass('is-valid');
23280             }
23281         }
23282     },
23283     
23284      /**
23285      * Mark this field as invalid
23286      * @param {String} msg The validation message
23287      */
23288     markInvalid : function(msg)
23289     {
23290         if(this.allowBlank){
23291             return;
23292         }
23293         
23294         var _this = this;
23295         
23296         this.fireEvent('invalid', this, msg);
23297         
23298         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23299         
23300         if(this.groupId){
23301             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23302         }
23303         
23304         if(label){
23305             label.markInvalid();
23306         }
23307             
23308         if(this.inputType == 'radio'){
23309             
23310             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23311                 var fg = e.findParent('.form-group', false, true);
23312                 if (Roo.bootstrap.version == 3) {
23313                     fg.removeClass([_this.invalidClass, _this.validClass]);
23314                     fg.addClass(_this.invalidClass);
23315                 } else {
23316                     fg.removeClass(['is-invalid', 'is-valid']);
23317                     fg.addClass('is-invalid');
23318                 }
23319             });
23320             
23321             return;
23322         }
23323         
23324         if(!this.groupId){
23325             var fg = this.el.findParent('.form-group', false, true);
23326             if (Roo.bootstrap.version == 3) {
23327                 fg.removeClass([_this.invalidClass, _this.validClass]);
23328                 fg.addClass(_this.invalidClass);
23329             } else {
23330                 fg.removeClass(['is-invalid', 'is-valid']);
23331                 fg.addClass('is-invalid');
23332             }
23333             return;
23334         }
23335         
23336         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23337         
23338         if(!group){
23339             return;
23340         }
23341         
23342         for(var i in group){
23343             var fg = group[i].el.findParent('.form-group', false, true);
23344             if (Roo.bootstrap.version == 3) {
23345                 fg.removeClass([_this.invalidClass, _this.validClass]);
23346                 fg.addClass(_this.invalidClass);
23347             } else {
23348                 fg.removeClass(['is-invalid', 'is-valid']);
23349                 fg.addClass('is-invalid');
23350             }
23351         }
23352         
23353     },
23354     
23355     clearInvalid : function()
23356     {
23357         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23358         
23359         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23360         
23361         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23362         
23363         if (label && label.iconEl) {
23364             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23365             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23366         }
23367     },
23368     
23369     disable : function()
23370     {
23371         if(this.inputType != 'radio'){
23372             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23373             return;
23374         }
23375         
23376         var _this = this;
23377         
23378         if(this.rendered){
23379             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23380                 _this.getActionEl().addClass(this.disabledClass);
23381                 e.dom.disabled = true;
23382             });
23383         }
23384         
23385         this.disabled = true;
23386         this.fireEvent("disable", this);
23387         return this;
23388     },
23389
23390     enable : function()
23391     {
23392         if(this.inputType != 'radio'){
23393             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23394             return;
23395         }
23396         
23397         var _this = this;
23398         
23399         if(this.rendered){
23400             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23401                 _this.getActionEl().removeClass(this.disabledClass);
23402                 e.dom.disabled = false;
23403             });
23404         }
23405         
23406         this.disabled = false;
23407         this.fireEvent("enable", this);
23408         return this;
23409     },
23410     
23411     setBoxLabel : function(v)
23412     {
23413         this.boxLabel = v;
23414         
23415         if(this.rendered){
23416             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23417         }
23418     }
23419
23420 });
23421
23422 Roo.apply(Roo.bootstrap.CheckBox, {
23423     
23424     groups: {},
23425     
23426      /**
23427     * register a CheckBox Group
23428     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23429     */
23430     register : function(checkbox)
23431     {
23432         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23433             this.groups[checkbox.groupId] = {};
23434         }
23435         
23436         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23437             return;
23438         }
23439         
23440         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23441         
23442     },
23443     /**
23444     * fetch a CheckBox Group based on the group ID
23445     * @param {string} the group ID
23446     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23447     */
23448     get: function(groupId) {
23449         if (typeof(this.groups[groupId]) == 'undefined') {
23450             return false;
23451         }
23452         
23453         return this.groups[groupId] ;
23454     }
23455     
23456     
23457 });
23458 /*
23459  * - LGPL
23460  *
23461  * RadioItem
23462  * 
23463  */
23464
23465 /**
23466  * @class Roo.bootstrap.Radio
23467  * @extends Roo.bootstrap.Component
23468  * Bootstrap Radio class
23469  * @cfg {String} boxLabel - the label associated
23470  * @cfg {String} value - the value of radio
23471  * 
23472  * @constructor
23473  * Create a new Radio
23474  * @param {Object} config The config object
23475  */
23476 Roo.bootstrap.Radio = function(config){
23477     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23478     
23479 };
23480
23481 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23482     
23483     boxLabel : '',
23484     
23485     value : '',
23486     
23487     getAutoCreate : function()
23488     {
23489         var cfg = {
23490             tag : 'div',
23491             cls : 'form-group radio',
23492             cn : [
23493                 {
23494                     tag : 'label',
23495                     cls : 'box-label',
23496                     html : this.boxLabel
23497                 }
23498             ]
23499         };
23500         
23501         return cfg;
23502     },
23503     
23504     initEvents : function() 
23505     {
23506         this.parent().register(this);
23507         
23508         this.el.on('click', this.onClick, this);
23509         
23510     },
23511     
23512     onClick : function(e)
23513     {
23514         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23515             this.setChecked(true);
23516         }
23517     },
23518     
23519     setChecked : function(state, suppressEvent)
23520     {
23521         this.parent().setValue(this.value, suppressEvent);
23522         
23523     },
23524     
23525     setBoxLabel : function(v)
23526     {
23527         this.boxLabel = v;
23528         
23529         if(this.rendered){
23530             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23531         }
23532     }
23533     
23534 });
23535  
23536
23537  /*
23538  * - LGPL
23539  *
23540  * Input
23541  * 
23542  */
23543
23544 /**
23545  * @class Roo.bootstrap.SecurePass
23546  * @extends Roo.bootstrap.Input
23547  * Bootstrap SecurePass class
23548  *
23549  * 
23550  * @constructor
23551  * Create a new SecurePass
23552  * @param {Object} config The config object
23553  */
23554  
23555 Roo.bootstrap.SecurePass = function (config) {
23556     // these go here, so the translation tool can replace them..
23557     this.errors = {
23558         PwdEmpty: "Please type a password, and then retype it to confirm.",
23559         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23560         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23561         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23562         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23563         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23564         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23565         TooWeak: "Your password is Too Weak."
23566     },
23567     this.meterLabel = "Password strength:";
23568     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23569     this.meterClass = [
23570         "roo-password-meter-tooweak", 
23571         "roo-password-meter-weak", 
23572         "roo-password-meter-medium", 
23573         "roo-password-meter-strong", 
23574         "roo-password-meter-grey"
23575     ];
23576     
23577     this.errors = {};
23578     
23579     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23580 }
23581
23582 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23583     /**
23584      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23585      * {
23586      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23587      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23588      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23589      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23590      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23591      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23592      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23593      * })
23594      */
23595     // private
23596     
23597     meterWidth: 300,
23598     errorMsg :'',    
23599     errors: false,
23600     imageRoot: '/',
23601     /**
23602      * @cfg {String/Object} Label for the strength meter (defaults to
23603      * 'Password strength:')
23604      */
23605     // private
23606     meterLabel: '',
23607     /**
23608      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23609      * ['Weak', 'Medium', 'Strong'])
23610      */
23611     // private    
23612     pwdStrengths: false,    
23613     // private
23614     strength: 0,
23615     // private
23616     _lastPwd: null,
23617     // private
23618     kCapitalLetter: 0,
23619     kSmallLetter: 1,
23620     kDigit: 2,
23621     kPunctuation: 3,
23622     
23623     insecure: false,
23624     // private
23625     initEvents: function ()
23626     {
23627         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23628
23629         if (this.el.is('input[type=password]') && Roo.isSafari) {
23630             this.el.on('keydown', this.SafariOnKeyDown, this);
23631         }
23632
23633         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23634     },
23635     // private
23636     onRender: function (ct, position)
23637     {
23638         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23639         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23640         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23641
23642         this.trigger.createChild({
23643                    cn: [
23644                     {
23645                     //id: 'PwdMeter',
23646                     tag: 'div',
23647                     cls: 'roo-password-meter-grey col-xs-12',
23648                     style: {
23649                         //width: 0,
23650                         //width: this.meterWidth + 'px'                                                
23651                         }
23652                     },
23653                     {                            
23654                          cls: 'roo-password-meter-text'                          
23655                     }
23656                 ]            
23657         });
23658
23659          
23660         if (this.hideTrigger) {
23661             this.trigger.setDisplayed(false);
23662         }
23663         this.setSize(this.width || '', this.height || '');
23664     },
23665     // private
23666     onDestroy: function ()
23667     {
23668         if (this.trigger) {
23669             this.trigger.removeAllListeners();
23670             this.trigger.remove();
23671         }
23672         if (this.wrap) {
23673             this.wrap.remove();
23674         }
23675         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23676     },
23677     // private
23678     checkStrength: function ()
23679     {
23680         var pwd = this.inputEl().getValue();
23681         if (pwd == this._lastPwd) {
23682             return;
23683         }
23684
23685         var strength;
23686         if (this.ClientSideStrongPassword(pwd)) {
23687             strength = 3;
23688         } else if (this.ClientSideMediumPassword(pwd)) {
23689             strength = 2;
23690         } else if (this.ClientSideWeakPassword(pwd)) {
23691             strength = 1;
23692         } else {
23693             strength = 0;
23694         }
23695         
23696         Roo.log('strength1: ' + strength);
23697         
23698         //var pm = this.trigger.child('div/div/div').dom;
23699         var pm = this.trigger.child('div/div');
23700         pm.removeClass(this.meterClass);
23701         pm.addClass(this.meterClass[strength]);
23702                 
23703         
23704         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23705                 
23706         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23707         
23708         this._lastPwd = pwd;
23709     },
23710     reset: function ()
23711     {
23712         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23713         
23714         this._lastPwd = '';
23715         
23716         var pm = this.trigger.child('div/div');
23717         pm.removeClass(this.meterClass);
23718         pm.addClass('roo-password-meter-grey');        
23719         
23720         
23721         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23722         
23723         pt.innerHTML = '';
23724         this.inputEl().dom.type='password';
23725     },
23726     // private
23727     validateValue: function (value)
23728     {
23729         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23730             return false;
23731         }
23732         if (value.length == 0) {
23733             if (this.allowBlank) {
23734                 this.clearInvalid();
23735                 return true;
23736             }
23737
23738             this.markInvalid(this.errors.PwdEmpty);
23739             this.errorMsg = this.errors.PwdEmpty;
23740             return false;
23741         }
23742         
23743         if(this.insecure){
23744             return true;
23745         }
23746         
23747         if (!value.match(/[\x21-\x7e]+/)) {
23748             this.markInvalid(this.errors.PwdBadChar);
23749             this.errorMsg = this.errors.PwdBadChar;
23750             return false;
23751         }
23752         if (value.length < 6) {
23753             this.markInvalid(this.errors.PwdShort);
23754             this.errorMsg = this.errors.PwdShort;
23755             return false;
23756         }
23757         if (value.length > 16) {
23758             this.markInvalid(this.errors.PwdLong);
23759             this.errorMsg = this.errors.PwdLong;
23760             return false;
23761         }
23762         var strength;
23763         if (this.ClientSideStrongPassword(value)) {
23764             strength = 3;
23765         } else if (this.ClientSideMediumPassword(value)) {
23766             strength = 2;
23767         } else if (this.ClientSideWeakPassword(value)) {
23768             strength = 1;
23769         } else {
23770             strength = 0;
23771         }
23772
23773         
23774         if (strength < 2) {
23775             //this.markInvalid(this.errors.TooWeak);
23776             this.errorMsg = this.errors.TooWeak;
23777             //return false;
23778         }
23779         
23780         
23781         console.log('strength2: ' + strength);
23782         
23783         //var pm = this.trigger.child('div/div/div').dom;
23784         
23785         var pm = this.trigger.child('div/div');
23786         pm.removeClass(this.meterClass);
23787         pm.addClass(this.meterClass[strength]);
23788                 
23789         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23790                 
23791         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23792         
23793         this.errorMsg = ''; 
23794         return true;
23795     },
23796     // private
23797     CharacterSetChecks: function (type)
23798     {
23799         this.type = type;
23800         this.fResult = false;
23801     },
23802     // private
23803     isctype: function (character, type)
23804     {
23805         switch (type) {  
23806             case this.kCapitalLetter:
23807                 if (character >= 'A' && character <= 'Z') {
23808                     return true;
23809                 }
23810                 break;
23811             
23812             case this.kSmallLetter:
23813                 if (character >= 'a' && character <= 'z') {
23814                     return true;
23815                 }
23816                 break;
23817             
23818             case this.kDigit:
23819                 if (character >= '0' && character <= '9') {
23820                     return true;
23821                 }
23822                 break;
23823             
23824             case this.kPunctuation:
23825                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23826                     return true;
23827                 }
23828                 break;
23829             
23830             default:
23831                 return false;
23832         }
23833
23834     },
23835     // private
23836     IsLongEnough: function (pwd, size)
23837     {
23838         return !(pwd == null || isNaN(size) || pwd.length < size);
23839     },
23840     // private
23841     SpansEnoughCharacterSets: function (word, nb)
23842     {
23843         if (!this.IsLongEnough(word, nb))
23844         {
23845             return false;
23846         }
23847
23848         var characterSetChecks = new Array(
23849             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23850             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23851         );
23852         
23853         for (var index = 0; index < word.length; ++index) {
23854             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23855                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23856                     characterSetChecks[nCharSet].fResult = true;
23857                     break;
23858                 }
23859             }
23860         }
23861
23862         var nCharSets = 0;
23863         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23864             if (characterSetChecks[nCharSet].fResult) {
23865                 ++nCharSets;
23866             }
23867         }
23868
23869         if (nCharSets < nb) {
23870             return false;
23871         }
23872         return true;
23873     },
23874     // private
23875     ClientSideStrongPassword: function (pwd)
23876     {
23877         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23878     },
23879     // private
23880     ClientSideMediumPassword: function (pwd)
23881     {
23882         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23883     },
23884     // private
23885     ClientSideWeakPassword: function (pwd)
23886     {
23887         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23888     }
23889           
23890 })//<script type="text/javascript">
23891
23892 /*
23893  * Based  Ext JS Library 1.1.1
23894  * Copyright(c) 2006-2007, Ext JS, LLC.
23895  * LGPL
23896  *
23897  */
23898  
23899 /**
23900  * @class Roo.HtmlEditorCore
23901  * @extends Roo.Component
23902  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23903  *
23904  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23905  */
23906
23907 Roo.HtmlEditorCore = function(config){
23908     
23909     
23910     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23911     
23912     
23913     this.addEvents({
23914         /**
23915          * @event initialize
23916          * Fires when the editor is fully initialized (including the iframe)
23917          * @param {Roo.HtmlEditorCore} this
23918          */
23919         initialize: true,
23920         /**
23921          * @event activate
23922          * Fires when the editor is first receives the focus. Any insertion must wait
23923          * until after this event.
23924          * @param {Roo.HtmlEditorCore} this
23925          */
23926         activate: true,
23927          /**
23928          * @event beforesync
23929          * Fires before the textarea is updated with content from the editor iframe. Return false
23930          * to cancel the sync.
23931          * @param {Roo.HtmlEditorCore} this
23932          * @param {String} html
23933          */
23934         beforesync: true,
23935          /**
23936          * @event beforepush
23937          * Fires before the iframe editor is updated with content from the textarea. Return false
23938          * to cancel the push.
23939          * @param {Roo.HtmlEditorCore} this
23940          * @param {String} html
23941          */
23942         beforepush: true,
23943          /**
23944          * @event sync
23945          * Fires when the textarea is updated with content from the editor iframe.
23946          * @param {Roo.HtmlEditorCore} this
23947          * @param {String} html
23948          */
23949         sync: true,
23950          /**
23951          * @event push
23952          * Fires when the iframe editor is updated with content from the textarea.
23953          * @param {Roo.HtmlEditorCore} this
23954          * @param {String} html
23955          */
23956         push: true,
23957         
23958         /**
23959          * @event editorevent
23960          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23961          * @param {Roo.HtmlEditorCore} this
23962          */
23963         editorevent: true
23964         
23965     });
23966     
23967     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23968     
23969     // defaults : white / black...
23970     this.applyBlacklists();
23971     
23972     
23973     
23974 };
23975
23976
23977 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23978
23979
23980      /**
23981      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23982      */
23983     
23984     owner : false,
23985     
23986      /**
23987      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23988      *                        Roo.resizable.
23989      */
23990     resizable : false,
23991      /**
23992      * @cfg {Number} height (in pixels)
23993      */   
23994     height: 300,
23995    /**
23996      * @cfg {Number} width (in pixels)
23997      */   
23998     width: 500,
23999     
24000     /**
24001      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24002      * 
24003      */
24004     stylesheets: false,
24005     
24006     // id of frame..
24007     frameId: false,
24008     
24009     // private properties
24010     validationEvent : false,
24011     deferHeight: true,
24012     initialized : false,
24013     activated : false,
24014     sourceEditMode : false,
24015     onFocus : Roo.emptyFn,
24016     iframePad:3,
24017     hideMode:'offsets',
24018     
24019     clearUp: true,
24020     
24021     // blacklist + whitelisted elements..
24022     black: false,
24023     white: false,
24024      
24025     bodyCls : '',
24026
24027     /**
24028      * Protected method that will not generally be called directly. It
24029      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24030      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24031      */
24032     getDocMarkup : function(){
24033         // body styles..
24034         var st = '';
24035         
24036         // inherit styels from page...?? 
24037         if (this.stylesheets === false) {
24038             
24039             Roo.get(document.head).select('style').each(function(node) {
24040                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24041             });
24042             
24043             Roo.get(document.head).select('link').each(function(node) { 
24044                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24045             });
24046             
24047         } else if (!this.stylesheets.length) {
24048                 // simple..
24049                 st = '<style type="text/css">' +
24050                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24051                    '</style>';
24052         } else {
24053             for (var i in this.stylesheets) { 
24054                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24055             }
24056             
24057         }
24058         
24059         st +=  '<style type="text/css">' +
24060             'IMG { cursor: pointer } ' +
24061         '</style>';
24062
24063         var cls = 'roo-htmleditor-body';
24064         
24065         if(this.bodyCls.length){
24066             cls += ' ' + this.bodyCls;
24067         }
24068         
24069         return '<html><head>' + st  +
24070             //<style type="text/css">' +
24071             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24072             //'</style>' +
24073             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24074     },
24075
24076     // private
24077     onRender : function(ct, position)
24078     {
24079         var _t = this;
24080         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24081         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24082         
24083         
24084         this.el.dom.style.border = '0 none';
24085         this.el.dom.setAttribute('tabIndex', -1);
24086         this.el.addClass('x-hidden hide');
24087         
24088         
24089         
24090         if(Roo.isIE){ // fix IE 1px bogus margin
24091             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24092         }
24093        
24094         
24095         this.frameId = Roo.id();
24096         
24097          
24098         
24099         var iframe = this.owner.wrap.createChild({
24100             tag: 'iframe',
24101             cls: 'form-control', // bootstrap..
24102             id: this.frameId,
24103             name: this.frameId,
24104             frameBorder : 'no',
24105             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24106         }, this.el
24107         );
24108         
24109         
24110         this.iframe = iframe.dom;
24111
24112          this.assignDocWin();
24113         
24114         this.doc.designMode = 'on';
24115        
24116         this.doc.open();
24117         this.doc.write(this.getDocMarkup());
24118         this.doc.close();
24119
24120         
24121         var task = { // must defer to wait for browser to be ready
24122             run : function(){
24123                 //console.log("run task?" + this.doc.readyState);
24124                 this.assignDocWin();
24125                 if(this.doc.body || this.doc.readyState == 'complete'){
24126                     try {
24127                         this.doc.designMode="on";
24128                     } catch (e) {
24129                         return;
24130                     }
24131                     Roo.TaskMgr.stop(task);
24132                     this.initEditor.defer(10, this);
24133                 }
24134             },
24135             interval : 10,
24136             duration: 10000,
24137             scope: this
24138         };
24139         Roo.TaskMgr.start(task);
24140
24141     },
24142
24143     // private
24144     onResize : function(w, h)
24145     {
24146          Roo.log('resize: ' +w + ',' + h );
24147         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24148         if(!this.iframe){
24149             return;
24150         }
24151         if(typeof w == 'number'){
24152             
24153             this.iframe.style.width = w + 'px';
24154         }
24155         if(typeof h == 'number'){
24156             
24157             this.iframe.style.height = h + 'px';
24158             if(this.doc){
24159                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24160             }
24161         }
24162         
24163     },
24164
24165     /**
24166      * Toggles the editor between standard and source edit mode.
24167      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24168      */
24169     toggleSourceEdit : function(sourceEditMode){
24170         
24171         this.sourceEditMode = sourceEditMode === true;
24172         
24173         if(this.sourceEditMode){
24174  
24175             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24176             
24177         }else{
24178             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24179             //this.iframe.className = '';
24180             this.deferFocus();
24181         }
24182         //this.setSize(this.owner.wrap.getSize());
24183         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24184     },
24185
24186     
24187   
24188
24189     /**
24190      * Protected method that will not generally be called directly. If you need/want
24191      * custom HTML cleanup, this is the method you should override.
24192      * @param {String} html The HTML to be cleaned
24193      * return {String} The cleaned HTML
24194      */
24195     cleanHtml : function(html){
24196         html = String(html);
24197         if(html.length > 5){
24198             if(Roo.isSafari){ // strip safari nonsense
24199                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24200             }
24201         }
24202         if(html == '&nbsp;'){
24203             html = '';
24204         }
24205         return html;
24206     },
24207
24208     /**
24209      * HTML Editor -> Textarea
24210      * Protected method that will not generally be called directly. Syncs the contents
24211      * of the editor iframe with the textarea.
24212      */
24213     syncValue : function(){
24214         if(this.initialized){
24215             var bd = (this.doc.body || this.doc.documentElement);
24216             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24217             var html = bd.innerHTML;
24218             if(Roo.isSafari){
24219                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24220                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24221                 if(m && m[1]){
24222                     html = '<div style="'+m[0]+'">' + html + '</div>';
24223                 }
24224             }
24225             html = this.cleanHtml(html);
24226             // fix up the special chars.. normaly like back quotes in word...
24227             // however we do not want to do this with chinese..
24228             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24229                 
24230                 var cc = match.charCodeAt();
24231
24232                 // Get the character value, handling surrogate pairs
24233                 if (match.length == 2) {
24234                     // It's a surrogate pair, calculate the Unicode code point
24235                     var high = match.charCodeAt(0) - 0xD800;
24236                     var low  = match.charCodeAt(1) - 0xDC00;
24237                     cc = (high * 0x400) + low + 0x10000;
24238                 }  else if (
24239                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24240                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24241                     (cc >= 0xf900 && cc < 0xfb00 )
24242                 ) {
24243                         return match;
24244                 }  
24245          
24246                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24247                 return "&#" + cc + ";";
24248                 
24249                 
24250             });
24251             
24252             
24253              
24254             if(this.owner.fireEvent('beforesync', this, html) !== false){
24255                 this.el.dom.value = html;
24256                 this.owner.fireEvent('sync', this, html);
24257             }
24258         }
24259     },
24260
24261     /**
24262      * Protected method that will not generally be called directly. Pushes the value of the textarea
24263      * into the iframe editor.
24264      */
24265     pushValue : function(){
24266         if(this.initialized){
24267             var v = this.el.dom.value.trim();
24268             
24269 //            if(v.length < 1){
24270 //                v = '&#160;';
24271 //            }
24272             
24273             if(this.owner.fireEvent('beforepush', this, v) !== false){
24274                 var d = (this.doc.body || this.doc.documentElement);
24275                 d.innerHTML = v;
24276                 this.cleanUpPaste();
24277                 this.el.dom.value = d.innerHTML;
24278                 this.owner.fireEvent('push', this, v);
24279             }
24280         }
24281     },
24282
24283     // private
24284     deferFocus : function(){
24285         this.focus.defer(10, this);
24286     },
24287
24288     // doc'ed in Field
24289     focus : function(){
24290         if(this.win && !this.sourceEditMode){
24291             this.win.focus();
24292         }else{
24293             this.el.focus();
24294         }
24295     },
24296     
24297     assignDocWin: function()
24298     {
24299         var iframe = this.iframe;
24300         
24301          if(Roo.isIE){
24302             this.doc = iframe.contentWindow.document;
24303             this.win = iframe.contentWindow;
24304         } else {
24305 //            if (!Roo.get(this.frameId)) {
24306 //                return;
24307 //            }
24308 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24309 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24310             
24311             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24312                 return;
24313             }
24314             
24315             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24316             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24317         }
24318     },
24319     
24320     // private
24321     initEditor : function(){
24322         //console.log("INIT EDITOR");
24323         this.assignDocWin();
24324         
24325         
24326         
24327         this.doc.designMode="on";
24328         this.doc.open();
24329         this.doc.write(this.getDocMarkup());
24330         this.doc.close();
24331         
24332         var dbody = (this.doc.body || this.doc.documentElement);
24333         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24334         // this copies styles from the containing element into thsi one..
24335         // not sure why we need all of this..
24336         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24337         
24338         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24339         //ss['background-attachment'] = 'fixed'; // w3c
24340         dbody.bgProperties = 'fixed'; // ie
24341         //Roo.DomHelper.applyStyles(dbody, ss);
24342         Roo.EventManager.on(this.doc, {
24343             //'mousedown': this.onEditorEvent,
24344             'mouseup': this.onEditorEvent,
24345             'dblclick': this.onEditorEvent,
24346             'click': this.onEditorEvent,
24347             'keyup': this.onEditorEvent,
24348             buffer:100,
24349             scope: this
24350         });
24351         if(Roo.isGecko){
24352             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24353         }
24354         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24355             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24356         }
24357         this.initialized = true;
24358
24359         this.owner.fireEvent('initialize', this);
24360         this.pushValue();
24361     },
24362
24363     // private
24364     onDestroy : function(){
24365         
24366         
24367         
24368         if(this.rendered){
24369             
24370             //for (var i =0; i < this.toolbars.length;i++) {
24371             //    // fixme - ask toolbars for heights?
24372             //    this.toolbars[i].onDestroy();
24373            // }
24374             
24375             //this.wrap.dom.innerHTML = '';
24376             //this.wrap.remove();
24377         }
24378     },
24379
24380     // private
24381     onFirstFocus : function(){
24382         
24383         this.assignDocWin();
24384         
24385         
24386         this.activated = true;
24387          
24388     
24389         if(Roo.isGecko){ // prevent silly gecko errors
24390             this.win.focus();
24391             var s = this.win.getSelection();
24392             if(!s.focusNode || s.focusNode.nodeType != 3){
24393                 var r = s.getRangeAt(0);
24394                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24395                 r.collapse(true);
24396                 this.deferFocus();
24397             }
24398             try{
24399                 this.execCmd('useCSS', true);
24400                 this.execCmd('styleWithCSS', false);
24401             }catch(e){}
24402         }
24403         this.owner.fireEvent('activate', this);
24404     },
24405
24406     // private
24407     adjustFont: function(btn){
24408         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24409         //if(Roo.isSafari){ // safari
24410         //    adjust *= 2;
24411        // }
24412         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24413         if(Roo.isSafari){ // safari
24414             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24415             v =  (v < 10) ? 10 : v;
24416             v =  (v > 48) ? 48 : v;
24417             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24418             
24419         }
24420         
24421         
24422         v = Math.max(1, v+adjust);
24423         
24424         this.execCmd('FontSize', v  );
24425     },
24426
24427     onEditorEvent : function(e)
24428     {
24429         this.owner.fireEvent('editorevent', this, e);
24430       //  this.updateToolbar();
24431         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24432     },
24433
24434     insertTag : function(tg)
24435     {
24436         // could be a bit smarter... -> wrap the current selected tRoo..
24437         if (tg.toLowerCase() == 'span' ||
24438             tg.toLowerCase() == 'code' ||
24439             tg.toLowerCase() == 'sup' ||
24440             tg.toLowerCase() == 'sub' 
24441             ) {
24442             
24443             range = this.createRange(this.getSelection());
24444             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24445             wrappingNode.appendChild(range.extractContents());
24446             range.insertNode(wrappingNode);
24447
24448             return;
24449             
24450             
24451             
24452         }
24453         this.execCmd("formatblock",   tg);
24454         
24455     },
24456     
24457     insertText : function(txt)
24458     {
24459         
24460         
24461         var range = this.createRange();
24462         range.deleteContents();
24463                //alert(Sender.getAttribute('label'));
24464                
24465         range.insertNode(this.doc.createTextNode(txt));
24466     } ,
24467     
24468      
24469
24470     /**
24471      * Executes a Midas editor command on the editor document and performs necessary focus and
24472      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24473      * @param {String} cmd The Midas command
24474      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24475      */
24476     relayCmd : function(cmd, value){
24477         this.win.focus();
24478         this.execCmd(cmd, value);
24479         this.owner.fireEvent('editorevent', this);
24480         //this.updateToolbar();
24481         this.owner.deferFocus();
24482     },
24483
24484     /**
24485      * Executes a Midas editor command directly on the editor document.
24486      * For visual commands, you should use {@link #relayCmd} instead.
24487      * <b>This should only be called after the editor is initialized.</b>
24488      * @param {String} cmd The Midas command
24489      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24490      */
24491     execCmd : function(cmd, value){
24492         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24493         this.syncValue();
24494     },
24495  
24496  
24497    
24498     /**
24499      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24500      * to insert tRoo.
24501      * @param {String} text | dom node.. 
24502      */
24503     insertAtCursor : function(text)
24504     {
24505         
24506         if(!this.activated){
24507             return;
24508         }
24509         /*
24510         if(Roo.isIE){
24511             this.win.focus();
24512             var r = this.doc.selection.createRange();
24513             if(r){
24514                 r.collapse(true);
24515                 r.pasteHTML(text);
24516                 this.syncValue();
24517                 this.deferFocus();
24518             
24519             }
24520             return;
24521         }
24522         */
24523         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24524             this.win.focus();
24525             
24526             
24527             // from jquery ui (MIT licenced)
24528             var range, node;
24529             var win = this.win;
24530             
24531             if (win.getSelection && win.getSelection().getRangeAt) {
24532                 range = win.getSelection().getRangeAt(0);
24533                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24534                 range.insertNode(node);
24535             } else if (win.document.selection && win.document.selection.createRange) {
24536                 // no firefox support
24537                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24538                 win.document.selection.createRange().pasteHTML(txt);
24539             } else {
24540                 // no firefox support
24541                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24542                 this.execCmd('InsertHTML', txt);
24543             } 
24544             
24545             this.syncValue();
24546             
24547             this.deferFocus();
24548         }
24549     },
24550  // private
24551     mozKeyPress : function(e){
24552         if(e.ctrlKey){
24553             var c = e.getCharCode(), cmd;
24554           
24555             if(c > 0){
24556                 c = String.fromCharCode(c).toLowerCase();
24557                 switch(c){
24558                     case 'b':
24559                         cmd = 'bold';
24560                         break;
24561                     case 'i':
24562                         cmd = 'italic';
24563                         break;
24564                     
24565                     case 'u':
24566                         cmd = 'underline';
24567                         break;
24568                     
24569                     case 'v':
24570                         this.cleanUpPaste.defer(100, this);
24571                         return;
24572                         
24573                 }
24574                 if(cmd){
24575                     this.win.focus();
24576                     this.execCmd(cmd);
24577                     this.deferFocus();
24578                     e.preventDefault();
24579                 }
24580                 
24581             }
24582         }
24583     },
24584
24585     // private
24586     fixKeys : function(){ // load time branching for fastest keydown performance
24587         if(Roo.isIE){
24588             return function(e){
24589                 var k = e.getKey(), r;
24590                 if(k == e.TAB){
24591                     e.stopEvent();
24592                     r = this.doc.selection.createRange();
24593                     if(r){
24594                         r.collapse(true);
24595                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24596                         this.deferFocus();
24597                     }
24598                     return;
24599                 }
24600                 
24601                 if(k == e.ENTER){
24602                     r = this.doc.selection.createRange();
24603                     if(r){
24604                         var target = r.parentElement();
24605                         if(!target || target.tagName.toLowerCase() != 'li'){
24606                             e.stopEvent();
24607                             r.pasteHTML('<br />');
24608                             r.collapse(false);
24609                             r.select();
24610                         }
24611                     }
24612                 }
24613                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24614                     this.cleanUpPaste.defer(100, this);
24615                     return;
24616                 }
24617                 
24618                 
24619             };
24620         }else if(Roo.isOpera){
24621             return function(e){
24622                 var k = e.getKey();
24623                 if(k == e.TAB){
24624                     e.stopEvent();
24625                     this.win.focus();
24626                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24627                     this.deferFocus();
24628                 }
24629                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24630                     this.cleanUpPaste.defer(100, this);
24631                     return;
24632                 }
24633                 
24634             };
24635         }else if(Roo.isSafari){
24636             return function(e){
24637                 var k = e.getKey();
24638                 
24639                 if(k == e.TAB){
24640                     e.stopEvent();
24641                     this.execCmd('InsertText','\t');
24642                     this.deferFocus();
24643                     return;
24644                 }
24645                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24646                     this.cleanUpPaste.defer(100, this);
24647                     return;
24648                 }
24649                 
24650              };
24651         }
24652     }(),
24653     
24654     getAllAncestors: function()
24655     {
24656         var p = this.getSelectedNode();
24657         var a = [];
24658         if (!p) {
24659             a.push(p); // push blank onto stack..
24660             p = this.getParentElement();
24661         }
24662         
24663         
24664         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24665             a.push(p);
24666             p = p.parentNode;
24667         }
24668         a.push(this.doc.body);
24669         return a;
24670     },
24671     lastSel : false,
24672     lastSelNode : false,
24673     
24674     
24675     getSelection : function() 
24676     {
24677         this.assignDocWin();
24678         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24679     },
24680     
24681     getSelectedNode: function() 
24682     {
24683         // this may only work on Gecko!!!
24684         
24685         // should we cache this!!!!
24686         
24687         
24688         
24689          
24690         var range = this.createRange(this.getSelection()).cloneRange();
24691         
24692         if (Roo.isIE) {
24693             var parent = range.parentElement();
24694             while (true) {
24695                 var testRange = range.duplicate();
24696                 testRange.moveToElementText(parent);
24697                 if (testRange.inRange(range)) {
24698                     break;
24699                 }
24700                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24701                     break;
24702                 }
24703                 parent = parent.parentElement;
24704             }
24705             return parent;
24706         }
24707         
24708         // is ancestor a text element.
24709         var ac =  range.commonAncestorContainer;
24710         if (ac.nodeType == 3) {
24711             ac = ac.parentNode;
24712         }
24713         
24714         var ar = ac.childNodes;
24715          
24716         var nodes = [];
24717         var other_nodes = [];
24718         var has_other_nodes = false;
24719         for (var i=0;i<ar.length;i++) {
24720             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24721                 continue;
24722             }
24723             // fullly contained node.
24724             
24725             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24726                 nodes.push(ar[i]);
24727                 continue;
24728             }
24729             
24730             // probably selected..
24731             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24732                 other_nodes.push(ar[i]);
24733                 continue;
24734             }
24735             // outer..
24736             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24737                 continue;
24738             }
24739             
24740             
24741             has_other_nodes = true;
24742         }
24743         if (!nodes.length && other_nodes.length) {
24744             nodes= other_nodes;
24745         }
24746         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24747             return false;
24748         }
24749         
24750         return nodes[0];
24751     },
24752     createRange: function(sel)
24753     {
24754         // this has strange effects when using with 
24755         // top toolbar - not sure if it's a great idea.
24756         //this.editor.contentWindow.focus();
24757         if (typeof sel != "undefined") {
24758             try {
24759                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24760             } catch(e) {
24761                 return this.doc.createRange();
24762             }
24763         } else {
24764             return this.doc.createRange();
24765         }
24766     },
24767     getParentElement: function()
24768     {
24769         
24770         this.assignDocWin();
24771         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24772         
24773         var range = this.createRange(sel);
24774          
24775         try {
24776             var p = range.commonAncestorContainer;
24777             while (p.nodeType == 3) { // text node
24778                 p = p.parentNode;
24779             }
24780             return p;
24781         } catch (e) {
24782             return null;
24783         }
24784     
24785     },
24786     /***
24787      *
24788      * Range intersection.. the hard stuff...
24789      *  '-1' = before
24790      *  '0' = hits..
24791      *  '1' = after.
24792      *         [ -- selected range --- ]
24793      *   [fail]                        [fail]
24794      *
24795      *    basically..
24796      *      if end is before start or  hits it. fail.
24797      *      if start is after end or hits it fail.
24798      *
24799      *   if either hits (but other is outside. - then it's not 
24800      *   
24801      *    
24802      **/
24803     
24804     
24805     // @see http://www.thismuchiknow.co.uk/?p=64.
24806     rangeIntersectsNode : function(range, node)
24807     {
24808         var nodeRange = node.ownerDocument.createRange();
24809         try {
24810             nodeRange.selectNode(node);
24811         } catch (e) {
24812             nodeRange.selectNodeContents(node);
24813         }
24814     
24815         var rangeStartRange = range.cloneRange();
24816         rangeStartRange.collapse(true);
24817     
24818         var rangeEndRange = range.cloneRange();
24819         rangeEndRange.collapse(false);
24820     
24821         var nodeStartRange = nodeRange.cloneRange();
24822         nodeStartRange.collapse(true);
24823     
24824         var nodeEndRange = nodeRange.cloneRange();
24825         nodeEndRange.collapse(false);
24826     
24827         return rangeStartRange.compareBoundaryPoints(
24828                  Range.START_TO_START, nodeEndRange) == -1 &&
24829                rangeEndRange.compareBoundaryPoints(
24830                  Range.START_TO_START, nodeStartRange) == 1;
24831         
24832          
24833     },
24834     rangeCompareNode : function(range, node)
24835     {
24836         var nodeRange = node.ownerDocument.createRange();
24837         try {
24838             nodeRange.selectNode(node);
24839         } catch (e) {
24840             nodeRange.selectNodeContents(node);
24841         }
24842         
24843         
24844         range.collapse(true);
24845     
24846         nodeRange.collapse(true);
24847      
24848         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24849         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24850          
24851         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24852         
24853         var nodeIsBefore   =  ss == 1;
24854         var nodeIsAfter    = ee == -1;
24855         
24856         if (nodeIsBefore && nodeIsAfter) {
24857             return 0; // outer
24858         }
24859         if (!nodeIsBefore && nodeIsAfter) {
24860             return 1; //right trailed.
24861         }
24862         
24863         if (nodeIsBefore && !nodeIsAfter) {
24864             return 2;  // left trailed.
24865         }
24866         // fully contined.
24867         return 3;
24868     },
24869
24870     // private? - in a new class?
24871     cleanUpPaste :  function()
24872     {
24873         // cleans up the whole document..
24874         Roo.log('cleanuppaste');
24875         
24876         this.cleanUpChildren(this.doc.body);
24877         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24878         if (clean != this.doc.body.innerHTML) {
24879             this.doc.body.innerHTML = clean;
24880         }
24881         
24882     },
24883     
24884     cleanWordChars : function(input) {// change the chars to hex code
24885         var he = Roo.HtmlEditorCore;
24886         
24887         var output = input;
24888         Roo.each(he.swapCodes, function(sw) { 
24889             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24890             
24891             output = output.replace(swapper, sw[1]);
24892         });
24893         
24894         return output;
24895     },
24896     
24897     
24898     cleanUpChildren : function (n)
24899     {
24900         if (!n.childNodes.length) {
24901             return;
24902         }
24903         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24904            this.cleanUpChild(n.childNodes[i]);
24905         }
24906     },
24907     
24908     
24909         
24910     
24911     cleanUpChild : function (node)
24912     {
24913         var ed = this;
24914         //console.log(node);
24915         if (node.nodeName == "#text") {
24916             // clean up silly Windows -- stuff?
24917             return; 
24918         }
24919         if (node.nodeName == "#comment") {
24920             node.parentNode.removeChild(node);
24921             // clean up silly Windows -- stuff?
24922             return; 
24923         }
24924         var lcname = node.tagName.toLowerCase();
24925         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24926         // whitelist of tags..
24927         
24928         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24929             // remove node.
24930             node.parentNode.removeChild(node);
24931             return;
24932             
24933         }
24934         
24935         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24936         
24937         // spans with no attributes - just remove them..
24938         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24939             remove_keep_children = true;
24940         }
24941         
24942         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24943         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24944         
24945         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24946         //    remove_keep_children = true;
24947         //}
24948         
24949         if (remove_keep_children) {
24950             this.cleanUpChildren(node);
24951             // inserts everything just before this node...
24952             while (node.childNodes.length) {
24953                 var cn = node.childNodes[0];
24954                 node.removeChild(cn);
24955                 node.parentNode.insertBefore(cn, node);
24956             }
24957             node.parentNode.removeChild(node);
24958             return;
24959         }
24960         
24961         if (!node.attributes || !node.attributes.length) {
24962             
24963           
24964             
24965             
24966             this.cleanUpChildren(node);
24967             return;
24968         }
24969         
24970         function cleanAttr(n,v)
24971         {
24972             
24973             if (v.match(/^\./) || v.match(/^\//)) {
24974                 return;
24975             }
24976             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24977                 return;
24978             }
24979             if (v.match(/^#/)) {
24980                 return;
24981             }
24982             if (v.match(/^\{/)) { // allow template editing.
24983                 return;
24984             }
24985 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24986             node.removeAttribute(n);
24987             
24988         }
24989         
24990         var cwhite = this.cwhite;
24991         var cblack = this.cblack;
24992             
24993         function cleanStyle(n,v)
24994         {
24995             if (v.match(/expression/)) { //XSS?? should we even bother..
24996                 node.removeAttribute(n);
24997                 return;
24998             }
24999             
25000             var parts = v.split(/;/);
25001             var clean = [];
25002             
25003             Roo.each(parts, function(p) {
25004                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25005                 if (!p.length) {
25006                     return true;
25007                 }
25008                 var l = p.split(':').shift().replace(/\s+/g,'');
25009                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25010                 
25011                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25012 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25013                     //node.removeAttribute(n);
25014                     return true;
25015                 }
25016                 //Roo.log()
25017                 // only allow 'c whitelisted system attributes'
25018                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25019 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25020                     //node.removeAttribute(n);
25021                     return true;
25022                 }
25023                 
25024                 
25025                  
25026                 
25027                 clean.push(p);
25028                 return true;
25029             });
25030             if (clean.length) { 
25031                 node.setAttribute(n, clean.join(';'));
25032             } else {
25033                 node.removeAttribute(n);
25034             }
25035             
25036         }
25037         
25038         
25039         for (var i = node.attributes.length-1; i > -1 ; i--) {
25040             var a = node.attributes[i];
25041             //console.log(a);
25042             
25043             if (a.name.toLowerCase().substr(0,2)=='on')  {
25044                 node.removeAttribute(a.name);
25045                 continue;
25046             }
25047             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25048                 node.removeAttribute(a.name);
25049                 continue;
25050             }
25051             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25052                 cleanAttr(a.name,a.value); // fixme..
25053                 continue;
25054             }
25055             if (a.name == 'style') {
25056                 cleanStyle(a.name,a.value);
25057                 continue;
25058             }
25059             /// clean up MS crap..
25060             // tecnically this should be a list of valid class'es..
25061             
25062             
25063             if (a.name == 'class') {
25064                 if (a.value.match(/^Mso/)) {
25065                     node.removeAttribute('class');
25066                 }
25067                 
25068                 if (a.value.match(/^body$/)) {
25069                     node.removeAttribute('class');
25070                 }
25071                 continue;
25072             }
25073             
25074             // style cleanup!?
25075             // class cleanup?
25076             
25077         }
25078         
25079         
25080         this.cleanUpChildren(node);
25081         
25082         
25083     },
25084     
25085     /**
25086      * Clean up MS wordisms...
25087      */
25088     cleanWord : function(node)
25089     {
25090         if (!node) {
25091             this.cleanWord(this.doc.body);
25092             return;
25093         }
25094         
25095         if(
25096                 node.nodeName == 'SPAN' &&
25097                 !node.hasAttributes() &&
25098                 node.childNodes.length == 1 &&
25099                 node.firstChild.nodeName == "#text"  
25100         ) {
25101             var textNode = node.firstChild;
25102             node.removeChild(textNode);
25103             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25104                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25105             }
25106             node.parentNode.insertBefore(textNode, node);
25107             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25108                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25109             }
25110             node.parentNode.removeChild(node);
25111         }
25112         
25113         if (node.nodeName == "#text") {
25114             // clean up silly Windows -- stuff?
25115             return; 
25116         }
25117         if (node.nodeName == "#comment") {
25118             node.parentNode.removeChild(node);
25119             // clean up silly Windows -- stuff?
25120             return; 
25121         }
25122         
25123         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25124             node.parentNode.removeChild(node);
25125             return;
25126         }
25127         //Roo.log(node.tagName);
25128         // remove - but keep children..
25129         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25130             //Roo.log('-- removed');
25131             while (node.childNodes.length) {
25132                 var cn = node.childNodes[0];
25133                 node.removeChild(cn);
25134                 node.parentNode.insertBefore(cn, node);
25135                 // move node to parent - and clean it..
25136                 this.cleanWord(cn);
25137             }
25138             node.parentNode.removeChild(node);
25139             /// no need to iterate chidlren = it's got none..
25140             //this.iterateChildren(node, this.cleanWord);
25141             return;
25142         }
25143         // clean styles
25144         if (node.className.length) {
25145             
25146             var cn = node.className.split(/\W+/);
25147             var cna = [];
25148             Roo.each(cn, function(cls) {
25149                 if (cls.match(/Mso[a-zA-Z]+/)) {
25150                     return;
25151                 }
25152                 cna.push(cls);
25153             });
25154             node.className = cna.length ? cna.join(' ') : '';
25155             if (!cna.length) {
25156                 node.removeAttribute("class");
25157             }
25158         }
25159         
25160         if (node.hasAttribute("lang")) {
25161             node.removeAttribute("lang");
25162         }
25163         
25164         if (node.hasAttribute("style")) {
25165             
25166             var styles = node.getAttribute("style").split(";");
25167             var nstyle = [];
25168             Roo.each(styles, function(s) {
25169                 if (!s.match(/:/)) {
25170                     return;
25171                 }
25172                 var kv = s.split(":");
25173                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25174                     return;
25175                 }
25176                 // what ever is left... we allow.
25177                 nstyle.push(s);
25178             });
25179             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25180             if (!nstyle.length) {
25181                 node.removeAttribute('style');
25182             }
25183         }
25184         this.iterateChildren(node, this.cleanWord);
25185         
25186         
25187         
25188     },
25189     /**
25190      * iterateChildren of a Node, calling fn each time, using this as the scole..
25191      * @param {DomNode} node node to iterate children of.
25192      * @param {Function} fn method of this class to call on each item.
25193      */
25194     iterateChildren : function(node, fn)
25195     {
25196         if (!node.childNodes.length) {
25197                 return;
25198         }
25199         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25200            fn.call(this, node.childNodes[i])
25201         }
25202     },
25203     
25204     
25205     /**
25206      * cleanTableWidths.
25207      *
25208      * Quite often pasting from word etc.. results in tables with column and widths.
25209      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25210      *
25211      */
25212     cleanTableWidths : function(node)
25213     {
25214          
25215          
25216         if (!node) {
25217             this.cleanTableWidths(this.doc.body);
25218             return;
25219         }
25220         
25221         // ignore list...
25222         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25223             return; 
25224         }
25225         Roo.log(node.tagName);
25226         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25227             this.iterateChildren(node, this.cleanTableWidths);
25228             return;
25229         }
25230         if (node.hasAttribute('width')) {
25231             node.removeAttribute('width');
25232         }
25233         
25234          
25235         if (node.hasAttribute("style")) {
25236             // pretty basic...
25237             
25238             var styles = node.getAttribute("style").split(";");
25239             var nstyle = [];
25240             Roo.each(styles, function(s) {
25241                 if (!s.match(/:/)) {
25242                     return;
25243                 }
25244                 var kv = s.split(":");
25245                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25246                     return;
25247                 }
25248                 // what ever is left... we allow.
25249                 nstyle.push(s);
25250             });
25251             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25252             if (!nstyle.length) {
25253                 node.removeAttribute('style');
25254             }
25255         }
25256         
25257         this.iterateChildren(node, this.cleanTableWidths);
25258         
25259         
25260     },
25261     
25262     
25263     
25264     
25265     domToHTML : function(currentElement, depth, nopadtext) {
25266         
25267         depth = depth || 0;
25268         nopadtext = nopadtext || false;
25269     
25270         if (!currentElement) {
25271             return this.domToHTML(this.doc.body);
25272         }
25273         
25274         //Roo.log(currentElement);
25275         var j;
25276         var allText = false;
25277         var nodeName = currentElement.nodeName;
25278         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25279         
25280         if  (nodeName == '#text') {
25281             
25282             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25283         }
25284         
25285         
25286         var ret = '';
25287         if (nodeName != 'BODY') {
25288              
25289             var i = 0;
25290             // Prints the node tagName, such as <A>, <IMG>, etc
25291             if (tagName) {
25292                 var attr = [];
25293                 for(i = 0; i < currentElement.attributes.length;i++) {
25294                     // quoting?
25295                     var aname = currentElement.attributes.item(i).name;
25296                     if (!currentElement.attributes.item(i).value.length) {
25297                         continue;
25298                     }
25299                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25300                 }
25301                 
25302                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25303             } 
25304             else {
25305                 
25306                 // eack
25307             }
25308         } else {
25309             tagName = false;
25310         }
25311         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25312             return ret;
25313         }
25314         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25315             nopadtext = true;
25316         }
25317         
25318         
25319         // Traverse the tree
25320         i = 0;
25321         var currentElementChild = currentElement.childNodes.item(i);
25322         var allText = true;
25323         var innerHTML  = '';
25324         lastnode = '';
25325         while (currentElementChild) {
25326             // Formatting code (indent the tree so it looks nice on the screen)
25327             var nopad = nopadtext;
25328             if (lastnode == 'SPAN') {
25329                 nopad  = true;
25330             }
25331             // text
25332             if  (currentElementChild.nodeName == '#text') {
25333                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25334                 toadd = nopadtext ? toadd : toadd.trim();
25335                 if (!nopad && toadd.length > 80) {
25336                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25337                 }
25338                 innerHTML  += toadd;
25339                 
25340                 i++;
25341                 currentElementChild = currentElement.childNodes.item(i);
25342                 lastNode = '';
25343                 continue;
25344             }
25345             allText = false;
25346             
25347             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25348                 
25349             // Recursively traverse the tree structure of the child node
25350             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25351             lastnode = currentElementChild.nodeName;
25352             i++;
25353             currentElementChild=currentElement.childNodes.item(i);
25354         }
25355         
25356         ret += innerHTML;
25357         
25358         if (!allText) {
25359                 // The remaining code is mostly for formatting the tree
25360             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25361         }
25362         
25363         
25364         if (tagName) {
25365             ret+= "</"+tagName+">";
25366         }
25367         return ret;
25368         
25369     },
25370         
25371     applyBlacklists : function()
25372     {
25373         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25374         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25375         
25376         this.white = [];
25377         this.black = [];
25378         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25379             if (b.indexOf(tag) > -1) {
25380                 return;
25381             }
25382             this.white.push(tag);
25383             
25384         }, this);
25385         
25386         Roo.each(w, function(tag) {
25387             if (b.indexOf(tag) > -1) {
25388                 return;
25389             }
25390             if (this.white.indexOf(tag) > -1) {
25391                 return;
25392             }
25393             this.white.push(tag);
25394             
25395         }, this);
25396         
25397         
25398         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25399             if (w.indexOf(tag) > -1) {
25400                 return;
25401             }
25402             this.black.push(tag);
25403             
25404         }, this);
25405         
25406         Roo.each(b, function(tag) {
25407             if (w.indexOf(tag) > -1) {
25408                 return;
25409             }
25410             if (this.black.indexOf(tag) > -1) {
25411                 return;
25412             }
25413             this.black.push(tag);
25414             
25415         }, this);
25416         
25417         
25418         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25419         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25420         
25421         this.cwhite = [];
25422         this.cblack = [];
25423         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25424             if (b.indexOf(tag) > -1) {
25425                 return;
25426             }
25427             this.cwhite.push(tag);
25428             
25429         }, this);
25430         
25431         Roo.each(w, function(tag) {
25432             if (b.indexOf(tag) > -1) {
25433                 return;
25434             }
25435             if (this.cwhite.indexOf(tag) > -1) {
25436                 return;
25437             }
25438             this.cwhite.push(tag);
25439             
25440         }, this);
25441         
25442         
25443         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25444             if (w.indexOf(tag) > -1) {
25445                 return;
25446             }
25447             this.cblack.push(tag);
25448             
25449         }, this);
25450         
25451         Roo.each(b, function(tag) {
25452             if (w.indexOf(tag) > -1) {
25453                 return;
25454             }
25455             if (this.cblack.indexOf(tag) > -1) {
25456                 return;
25457             }
25458             this.cblack.push(tag);
25459             
25460         }, this);
25461     },
25462     
25463     setStylesheets : function(stylesheets)
25464     {
25465         if(typeof(stylesheets) == 'string'){
25466             Roo.get(this.iframe.contentDocument.head).createChild({
25467                 tag : 'link',
25468                 rel : 'stylesheet',
25469                 type : 'text/css',
25470                 href : stylesheets
25471             });
25472             
25473             return;
25474         }
25475         var _this = this;
25476      
25477         Roo.each(stylesheets, function(s) {
25478             if(!s.length){
25479                 return;
25480             }
25481             
25482             Roo.get(_this.iframe.contentDocument.head).createChild({
25483                 tag : 'link',
25484                 rel : 'stylesheet',
25485                 type : 'text/css',
25486                 href : s
25487             });
25488         });
25489
25490         
25491     },
25492     
25493     removeStylesheets : function()
25494     {
25495         var _this = this;
25496         
25497         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25498             s.remove();
25499         });
25500     },
25501     
25502     setStyle : function(style)
25503     {
25504         Roo.get(this.iframe.contentDocument.head).createChild({
25505             tag : 'style',
25506             type : 'text/css',
25507             html : style
25508         });
25509
25510         return;
25511     }
25512     
25513     // hide stuff that is not compatible
25514     /**
25515      * @event blur
25516      * @hide
25517      */
25518     /**
25519      * @event change
25520      * @hide
25521      */
25522     /**
25523      * @event focus
25524      * @hide
25525      */
25526     /**
25527      * @event specialkey
25528      * @hide
25529      */
25530     /**
25531      * @cfg {String} fieldClass @hide
25532      */
25533     /**
25534      * @cfg {String} focusClass @hide
25535      */
25536     /**
25537      * @cfg {String} autoCreate @hide
25538      */
25539     /**
25540      * @cfg {String} inputType @hide
25541      */
25542     /**
25543      * @cfg {String} invalidClass @hide
25544      */
25545     /**
25546      * @cfg {String} invalidText @hide
25547      */
25548     /**
25549      * @cfg {String} msgFx @hide
25550      */
25551     /**
25552      * @cfg {String} validateOnBlur @hide
25553      */
25554 });
25555
25556 Roo.HtmlEditorCore.white = [
25557         'area', 'br', 'img', 'input', 'hr', 'wbr',
25558         
25559        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25560        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25561        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25562        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25563        'table',   'ul',         'xmp', 
25564        
25565        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25566       'thead',   'tr', 
25567      
25568       'dir', 'menu', 'ol', 'ul', 'dl',
25569        
25570       'embed',  'object'
25571 ];
25572
25573
25574 Roo.HtmlEditorCore.black = [
25575     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25576         'applet', // 
25577         'base',   'basefont', 'bgsound', 'blink',  'body', 
25578         'frame',  'frameset', 'head',    'html',   'ilayer', 
25579         'iframe', 'layer',  'link',     'meta',    'object',   
25580         'script', 'style' ,'title',  'xml' // clean later..
25581 ];
25582 Roo.HtmlEditorCore.clean = [
25583     'script', 'style', 'title', 'xml'
25584 ];
25585 Roo.HtmlEditorCore.remove = [
25586     'font'
25587 ];
25588 // attributes..
25589
25590 Roo.HtmlEditorCore.ablack = [
25591     'on'
25592 ];
25593     
25594 Roo.HtmlEditorCore.aclean = [ 
25595     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25596 ];
25597
25598 // protocols..
25599 Roo.HtmlEditorCore.pwhite= [
25600         'http',  'https',  'mailto'
25601 ];
25602
25603 // white listed style attributes.
25604 Roo.HtmlEditorCore.cwhite= [
25605       //  'text-align', /// default is to allow most things..
25606       
25607          
25608 //        'font-size'//??
25609 ];
25610
25611 // black listed style attributes.
25612 Roo.HtmlEditorCore.cblack= [
25613       //  'font-size' -- this can be set by the project 
25614 ];
25615
25616
25617 Roo.HtmlEditorCore.swapCodes   =[ 
25618     [    8211, "--" ], 
25619     [    8212, "--" ], 
25620     [    8216,  "'" ],  
25621     [    8217, "'" ],  
25622     [    8220, '"' ],  
25623     [    8221, '"' ],  
25624     [    8226, "*" ],  
25625     [    8230, "..." ]
25626 ]; 
25627
25628     /*
25629  * - LGPL
25630  *
25631  * HtmlEditor
25632  * 
25633  */
25634
25635 /**
25636  * @class Roo.bootstrap.HtmlEditor
25637  * @extends Roo.bootstrap.TextArea
25638  * Bootstrap HtmlEditor class
25639
25640  * @constructor
25641  * Create a new HtmlEditor
25642  * @param {Object} config The config object
25643  */
25644
25645 Roo.bootstrap.HtmlEditor = function(config){
25646     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25647     if (!this.toolbars) {
25648         this.toolbars = [];
25649     }
25650     
25651     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25652     this.addEvents({
25653             /**
25654              * @event initialize
25655              * Fires when the editor is fully initialized (including the iframe)
25656              * @param {HtmlEditor} this
25657              */
25658             initialize: true,
25659             /**
25660              * @event activate
25661              * Fires when the editor is first receives the focus. Any insertion must wait
25662              * until after this event.
25663              * @param {HtmlEditor} this
25664              */
25665             activate: true,
25666              /**
25667              * @event beforesync
25668              * Fires before the textarea is updated with content from the editor iframe. Return false
25669              * to cancel the sync.
25670              * @param {HtmlEditor} this
25671              * @param {String} html
25672              */
25673             beforesync: true,
25674              /**
25675              * @event beforepush
25676              * Fires before the iframe editor is updated with content from the textarea. Return false
25677              * to cancel the push.
25678              * @param {HtmlEditor} this
25679              * @param {String} html
25680              */
25681             beforepush: true,
25682              /**
25683              * @event sync
25684              * Fires when the textarea is updated with content from the editor iframe.
25685              * @param {HtmlEditor} this
25686              * @param {String} html
25687              */
25688             sync: true,
25689              /**
25690              * @event push
25691              * Fires when the iframe editor is updated with content from the textarea.
25692              * @param {HtmlEditor} this
25693              * @param {String} html
25694              */
25695             push: true,
25696              /**
25697              * @event editmodechange
25698              * Fires when the editor switches edit modes
25699              * @param {HtmlEditor} this
25700              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25701              */
25702             editmodechange: true,
25703             /**
25704              * @event editorevent
25705              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25706              * @param {HtmlEditor} this
25707              */
25708             editorevent: true,
25709             /**
25710              * @event firstfocus
25711              * Fires when on first focus - needed by toolbars..
25712              * @param {HtmlEditor} this
25713              */
25714             firstfocus: true,
25715             /**
25716              * @event autosave
25717              * Auto save the htmlEditor value as a file into Events
25718              * @param {HtmlEditor} this
25719              */
25720             autosave: true,
25721             /**
25722              * @event savedpreview
25723              * preview the saved version of htmlEditor
25724              * @param {HtmlEditor} this
25725              */
25726             savedpreview: true
25727         });
25728 };
25729
25730
25731 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25732     
25733     
25734       /**
25735      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25736      */
25737     toolbars : false,
25738     
25739      /**
25740     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25741     */
25742     btns : [],
25743    
25744      /**
25745      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25746      *                        Roo.resizable.
25747      */
25748     resizable : false,
25749      /**
25750      * @cfg {Number} height (in pixels)
25751      */   
25752     height: 300,
25753    /**
25754      * @cfg {Number} width (in pixels)
25755      */   
25756     width: false,
25757     
25758     /**
25759      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25760      * 
25761      */
25762     stylesheets: false,
25763     
25764     // id of frame..
25765     frameId: false,
25766     
25767     // private properties
25768     validationEvent : false,
25769     deferHeight: true,
25770     initialized : false,
25771     activated : false,
25772     
25773     onFocus : Roo.emptyFn,
25774     iframePad:3,
25775     hideMode:'offsets',
25776     
25777     tbContainer : false,
25778     
25779     bodyCls : '',
25780     
25781     toolbarContainer :function() {
25782         return this.wrap.select('.x-html-editor-tb',true).first();
25783     },
25784
25785     /**
25786      * Protected method that will not generally be called directly. It
25787      * is called when the editor creates its toolbar. Override this method if you need to
25788      * add custom toolbar buttons.
25789      * @param {HtmlEditor} editor
25790      */
25791     createToolbar : function(){
25792         Roo.log('renewing');
25793         Roo.log("create toolbars");
25794         
25795         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25796         this.toolbars[0].render(this.toolbarContainer());
25797         
25798         return;
25799         
25800 //        if (!editor.toolbars || !editor.toolbars.length) {
25801 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25802 //        }
25803 //        
25804 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25805 //            editor.toolbars[i] = Roo.factory(
25806 //                    typeof(editor.toolbars[i]) == 'string' ?
25807 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25808 //                Roo.bootstrap.HtmlEditor);
25809 //            editor.toolbars[i].init(editor);
25810 //        }
25811     },
25812
25813      
25814     // private
25815     onRender : function(ct, position)
25816     {
25817        // Roo.log("Call onRender: " + this.xtype);
25818         var _t = this;
25819         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25820       
25821         this.wrap = this.inputEl().wrap({
25822             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25823         });
25824         
25825         this.editorcore.onRender(ct, position);
25826          
25827         if (this.resizable) {
25828             this.resizeEl = new Roo.Resizable(this.wrap, {
25829                 pinned : true,
25830                 wrap: true,
25831                 dynamic : true,
25832                 minHeight : this.height,
25833                 height: this.height,
25834                 handles : this.resizable,
25835                 width: this.width,
25836                 listeners : {
25837                     resize : function(r, w, h) {
25838                         _t.onResize(w,h); // -something
25839                     }
25840                 }
25841             });
25842             
25843         }
25844         this.createToolbar(this);
25845        
25846         
25847         if(!this.width && this.resizable){
25848             this.setSize(this.wrap.getSize());
25849         }
25850         if (this.resizeEl) {
25851             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25852             // should trigger onReize..
25853         }
25854         
25855     },
25856
25857     // private
25858     onResize : function(w, h)
25859     {
25860         Roo.log('resize: ' +w + ',' + h );
25861         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25862         var ew = false;
25863         var eh = false;
25864         
25865         if(this.inputEl() ){
25866             if(typeof w == 'number'){
25867                 var aw = w - this.wrap.getFrameWidth('lr');
25868                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25869                 ew = aw;
25870             }
25871             if(typeof h == 'number'){
25872                  var tbh = -11;  // fixme it needs to tool bar size!
25873                 for (var i =0; i < this.toolbars.length;i++) {
25874                     // fixme - ask toolbars for heights?
25875                     tbh += this.toolbars[i].el.getHeight();
25876                     //if (this.toolbars[i].footer) {
25877                     //    tbh += this.toolbars[i].footer.el.getHeight();
25878                     //}
25879                 }
25880               
25881                 
25882                 
25883                 
25884                 
25885                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25886                 ah -= 5; // knock a few pixes off for look..
25887                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25888                 var eh = ah;
25889             }
25890         }
25891         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25892         this.editorcore.onResize(ew,eh);
25893         
25894     },
25895
25896     /**
25897      * Toggles the editor between standard and source edit mode.
25898      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25899      */
25900     toggleSourceEdit : function(sourceEditMode)
25901     {
25902         this.editorcore.toggleSourceEdit(sourceEditMode);
25903         
25904         if(this.editorcore.sourceEditMode){
25905             Roo.log('editor - showing textarea');
25906             
25907 //            Roo.log('in');
25908 //            Roo.log(this.syncValue());
25909             this.syncValue();
25910             this.inputEl().removeClass(['hide', 'x-hidden']);
25911             this.inputEl().dom.removeAttribute('tabIndex');
25912             this.inputEl().focus();
25913         }else{
25914             Roo.log('editor - hiding textarea');
25915 //            Roo.log('out')
25916 //            Roo.log(this.pushValue()); 
25917             this.pushValue();
25918             
25919             this.inputEl().addClass(['hide', 'x-hidden']);
25920             this.inputEl().dom.setAttribute('tabIndex', -1);
25921             //this.deferFocus();
25922         }
25923          
25924         if(this.resizable){
25925             this.setSize(this.wrap.getSize());
25926         }
25927         
25928         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25929     },
25930  
25931     // private (for BoxComponent)
25932     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25933
25934     // private (for BoxComponent)
25935     getResizeEl : function(){
25936         return this.wrap;
25937     },
25938
25939     // private (for BoxComponent)
25940     getPositionEl : function(){
25941         return this.wrap;
25942     },
25943
25944     // private
25945     initEvents : function(){
25946         this.originalValue = this.getValue();
25947     },
25948
25949 //    /**
25950 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25951 //     * @method
25952 //     */
25953 //    markInvalid : Roo.emptyFn,
25954 //    /**
25955 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25956 //     * @method
25957 //     */
25958 //    clearInvalid : Roo.emptyFn,
25959
25960     setValue : function(v){
25961         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25962         this.editorcore.pushValue();
25963     },
25964
25965      
25966     // private
25967     deferFocus : function(){
25968         this.focus.defer(10, this);
25969     },
25970
25971     // doc'ed in Field
25972     focus : function(){
25973         this.editorcore.focus();
25974         
25975     },
25976       
25977
25978     // private
25979     onDestroy : function(){
25980         
25981         
25982         
25983         if(this.rendered){
25984             
25985             for (var i =0; i < this.toolbars.length;i++) {
25986                 // fixme - ask toolbars for heights?
25987                 this.toolbars[i].onDestroy();
25988             }
25989             
25990             this.wrap.dom.innerHTML = '';
25991             this.wrap.remove();
25992         }
25993     },
25994
25995     // private
25996     onFirstFocus : function(){
25997         //Roo.log("onFirstFocus");
25998         this.editorcore.onFirstFocus();
25999          for (var i =0; i < this.toolbars.length;i++) {
26000             this.toolbars[i].onFirstFocus();
26001         }
26002         
26003     },
26004     
26005     // private
26006     syncValue : function()
26007     {   
26008         this.editorcore.syncValue();
26009     },
26010     
26011     pushValue : function()
26012     {   
26013         this.editorcore.pushValue();
26014     }
26015      
26016     
26017     // hide stuff that is not compatible
26018     /**
26019      * @event blur
26020      * @hide
26021      */
26022     /**
26023      * @event change
26024      * @hide
26025      */
26026     /**
26027      * @event focus
26028      * @hide
26029      */
26030     /**
26031      * @event specialkey
26032      * @hide
26033      */
26034     /**
26035      * @cfg {String} fieldClass @hide
26036      */
26037     /**
26038      * @cfg {String} focusClass @hide
26039      */
26040     /**
26041      * @cfg {String} autoCreate @hide
26042      */
26043     /**
26044      * @cfg {String} inputType @hide
26045      */
26046      
26047     /**
26048      * @cfg {String} invalidText @hide
26049      */
26050     /**
26051      * @cfg {String} msgFx @hide
26052      */
26053     /**
26054      * @cfg {String} validateOnBlur @hide
26055      */
26056 });
26057  
26058     
26059    
26060    
26061    
26062       
26063 Roo.namespace('Roo.bootstrap.htmleditor');
26064 /**
26065  * @class Roo.bootstrap.HtmlEditorToolbar1
26066  * Basic Toolbar
26067  * 
26068  * @example
26069  * Usage:
26070  *
26071  new Roo.bootstrap.HtmlEditor({
26072     ....
26073     toolbars : [
26074         new Roo.bootstrap.HtmlEditorToolbar1({
26075             disable : { fonts: 1 , format: 1, ..., ... , ...],
26076             btns : [ .... ]
26077         })
26078     }
26079      
26080  * 
26081  * @cfg {Object} disable List of elements to disable..
26082  * @cfg {Array} btns List of additional buttons.
26083  * 
26084  * 
26085  * NEEDS Extra CSS? 
26086  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26087  */
26088  
26089 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26090 {
26091     
26092     Roo.apply(this, config);
26093     
26094     // default disabled, based on 'good practice'..
26095     this.disable = this.disable || {};
26096     Roo.applyIf(this.disable, {
26097         fontSize : true,
26098         colors : true,
26099         specialElements : true
26100     });
26101     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26102     
26103     this.editor = config.editor;
26104     this.editorcore = config.editor.editorcore;
26105     
26106     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26107     
26108     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26109     // dont call parent... till later.
26110 }
26111 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26112      
26113     bar : true,
26114     
26115     editor : false,
26116     editorcore : false,
26117     
26118     
26119     formats : [
26120         "p" ,  
26121         "h1","h2","h3","h4","h5","h6", 
26122         "pre", "code", 
26123         "abbr", "acronym", "address", "cite", "samp", "var",
26124         'div','span'
26125     ],
26126     
26127     onRender : function(ct, position)
26128     {
26129        // Roo.log("Call onRender: " + this.xtype);
26130         
26131        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26132        Roo.log(this.el);
26133        this.el.dom.style.marginBottom = '0';
26134        var _this = this;
26135        var editorcore = this.editorcore;
26136        var editor= this.editor;
26137        
26138        var children = [];
26139        var btn = function(id,cmd , toggle, handler, html){
26140        
26141             var  event = toggle ? 'toggle' : 'click';
26142        
26143             var a = {
26144                 size : 'sm',
26145                 xtype: 'Button',
26146                 xns: Roo.bootstrap,
26147                 //glyphicon : id,
26148                 fa: id,
26149                 cmd : id || cmd,
26150                 enableToggle:toggle !== false,
26151                 html : html || '',
26152                 pressed : toggle ? false : null,
26153                 listeners : {}
26154             };
26155             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26156                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26157             };
26158             children.push(a);
26159             return a;
26160        }
26161        
26162     //    var cb_box = function...
26163         
26164         var style = {
26165                 xtype: 'Button',
26166                 size : 'sm',
26167                 xns: Roo.bootstrap,
26168                 fa : 'font',
26169                 //html : 'submit'
26170                 menu : {
26171                     xtype: 'Menu',
26172                     xns: Roo.bootstrap,
26173                     items:  []
26174                 }
26175         };
26176         Roo.each(this.formats, function(f) {
26177             style.menu.items.push({
26178                 xtype :'MenuItem',
26179                 xns: Roo.bootstrap,
26180                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26181                 tagname : f,
26182                 listeners : {
26183                     click : function()
26184                     {
26185                         editorcore.insertTag(this.tagname);
26186                         editor.focus();
26187                     }
26188                 }
26189                 
26190             });
26191         });
26192         children.push(style);   
26193         
26194         btn('bold',false,true);
26195         btn('italic',false,true);
26196         btn('align-left', 'justifyleft',true);
26197         btn('align-center', 'justifycenter',true);
26198         btn('align-right' , 'justifyright',true);
26199         btn('link', false, false, function(btn) {
26200             //Roo.log("create link?");
26201             var url = prompt(this.createLinkText, this.defaultLinkValue);
26202             if(url && url != 'http:/'+'/'){
26203                 this.editorcore.relayCmd('createlink', url);
26204             }
26205         }),
26206         btn('list','insertunorderedlist',true);
26207         btn('pencil', false,true, function(btn){
26208                 Roo.log(this);
26209                 this.toggleSourceEdit(btn.pressed);
26210         });
26211         
26212         if (this.editor.btns.length > 0) {
26213             for (var i = 0; i<this.editor.btns.length; i++) {
26214                 children.push(this.editor.btns[i]);
26215             }
26216         }
26217         
26218         /*
26219         var cog = {
26220                 xtype: 'Button',
26221                 size : 'sm',
26222                 xns: Roo.bootstrap,
26223                 glyphicon : 'cog',
26224                 //html : 'submit'
26225                 menu : {
26226                     xtype: 'Menu',
26227                     xns: Roo.bootstrap,
26228                     items:  []
26229                 }
26230         };
26231         
26232         cog.menu.items.push({
26233             xtype :'MenuItem',
26234             xns: Roo.bootstrap,
26235             html : Clean styles,
26236             tagname : f,
26237             listeners : {
26238                 click : function()
26239                 {
26240                     editorcore.insertTag(this.tagname);
26241                     editor.focus();
26242                 }
26243             }
26244             
26245         });
26246        */
26247         
26248          
26249        this.xtype = 'NavSimplebar';
26250         
26251         for(var i=0;i< children.length;i++) {
26252             
26253             this.buttons.add(this.addxtypeChild(children[i]));
26254             
26255         }
26256         
26257         editor.on('editorevent', this.updateToolbar, this);
26258     },
26259     onBtnClick : function(id)
26260     {
26261        this.editorcore.relayCmd(id);
26262        this.editorcore.focus();
26263     },
26264     
26265     /**
26266      * Protected method that will not generally be called directly. It triggers
26267      * a toolbar update by reading the markup state of the current selection in the editor.
26268      */
26269     updateToolbar: function(){
26270
26271         if(!this.editorcore.activated){
26272             this.editor.onFirstFocus(); // is this neeed?
26273             return;
26274         }
26275
26276         var btns = this.buttons; 
26277         var doc = this.editorcore.doc;
26278         btns.get('bold').setActive(doc.queryCommandState('bold'));
26279         btns.get('italic').setActive(doc.queryCommandState('italic'));
26280         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26281         
26282         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26283         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26284         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26285         
26286         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26287         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26288          /*
26289         
26290         var ans = this.editorcore.getAllAncestors();
26291         if (this.formatCombo) {
26292             
26293             
26294             var store = this.formatCombo.store;
26295             this.formatCombo.setValue("");
26296             for (var i =0; i < ans.length;i++) {
26297                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26298                     // select it..
26299                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26300                     break;
26301                 }
26302             }
26303         }
26304         
26305         
26306         
26307         // hides menus... - so this cant be on a menu...
26308         Roo.bootstrap.MenuMgr.hideAll();
26309         */
26310         Roo.bootstrap.MenuMgr.hideAll();
26311         //this.editorsyncValue();
26312     },
26313     onFirstFocus: function() {
26314         this.buttons.each(function(item){
26315            item.enable();
26316         });
26317     },
26318     toggleSourceEdit : function(sourceEditMode){
26319         
26320           
26321         if(sourceEditMode){
26322             Roo.log("disabling buttons");
26323            this.buttons.each( function(item){
26324                 if(item.cmd != 'pencil'){
26325                     item.disable();
26326                 }
26327             });
26328           
26329         }else{
26330             Roo.log("enabling buttons");
26331             if(this.editorcore.initialized){
26332                 this.buttons.each( function(item){
26333                     item.enable();
26334                 });
26335             }
26336             
26337         }
26338         Roo.log("calling toggole on editor");
26339         // tell the editor that it's been pressed..
26340         this.editor.toggleSourceEdit(sourceEditMode);
26341        
26342     }
26343 });
26344
26345
26346
26347
26348  
26349 /*
26350  * - LGPL
26351  */
26352
26353 /**
26354  * @class Roo.bootstrap.Markdown
26355  * @extends Roo.bootstrap.TextArea
26356  * Bootstrap Showdown editable area
26357  * @cfg {string} content
26358  * 
26359  * @constructor
26360  * Create a new Showdown
26361  */
26362
26363 Roo.bootstrap.Markdown = function(config){
26364     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26365    
26366 };
26367
26368 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26369     
26370     editing :false,
26371     
26372     initEvents : function()
26373     {
26374         
26375         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26376         this.markdownEl = this.el.createChild({
26377             cls : 'roo-markdown-area'
26378         });
26379         this.inputEl().addClass('d-none');
26380         if (this.getValue() == '') {
26381             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26382             
26383         } else {
26384             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26385         }
26386         this.markdownEl.on('click', this.toggleTextEdit, this);
26387         this.on('blur', this.toggleTextEdit, this);
26388         this.on('specialkey', this.resizeTextArea, this);
26389     },
26390     
26391     toggleTextEdit : function()
26392     {
26393         var sh = this.markdownEl.getHeight();
26394         this.inputEl().addClass('d-none');
26395         this.markdownEl.addClass('d-none');
26396         if (!this.editing) {
26397             // show editor?
26398             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26399             this.inputEl().removeClass('d-none');
26400             this.inputEl().focus();
26401             this.editing = true;
26402             return;
26403         }
26404         // show showdown...
26405         this.updateMarkdown();
26406         this.markdownEl.removeClass('d-none');
26407         this.editing = false;
26408         return;
26409     },
26410     updateMarkdown : function()
26411     {
26412         if (this.getValue() == '') {
26413             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26414             return;
26415         }
26416  
26417         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26418     },
26419     
26420     resizeTextArea: function () {
26421         
26422         var sh = 100;
26423         Roo.log([sh, this.getValue().split("\n").length * 30]);
26424         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26425     },
26426     setValue : function(val)
26427     {
26428         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26429         if (!this.editing) {
26430             this.updateMarkdown();
26431         }
26432         
26433     },
26434     focus : function()
26435     {
26436         if (!this.editing) {
26437             this.toggleTextEdit();
26438         }
26439         
26440     }
26441
26442
26443 });
26444 /**
26445  * @class Roo.bootstrap.Table.AbstractSelectionModel
26446  * @extends Roo.util.Observable
26447  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26448  * implemented by descendant classes.  This class should not be directly instantiated.
26449  * @constructor
26450  */
26451 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26452     this.locked = false;
26453     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26454 };
26455
26456
26457 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26458     /** @ignore Called by the grid automatically. Do not call directly. */
26459     init : function(grid){
26460         this.grid = grid;
26461         this.initEvents();
26462     },
26463
26464     /**
26465      * Locks the selections.
26466      */
26467     lock : function(){
26468         this.locked = true;
26469     },
26470
26471     /**
26472      * Unlocks the selections.
26473      */
26474     unlock : function(){
26475         this.locked = false;
26476     },
26477
26478     /**
26479      * Returns true if the selections are locked.
26480      * @return {Boolean}
26481      */
26482     isLocked : function(){
26483         return this.locked;
26484     },
26485     
26486     
26487     initEvents : function ()
26488     {
26489         
26490     }
26491 });
26492 /**
26493  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26494  * @class Roo.bootstrap.Table.RowSelectionModel
26495  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26496  * It supports multiple selections and keyboard selection/navigation. 
26497  * @constructor
26498  * @param {Object} config
26499  */
26500
26501 Roo.bootstrap.Table.RowSelectionModel = function(config){
26502     Roo.apply(this, config);
26503     this.selections = new Roo.util.MixedCollection(false, function(o){
26504         return o.id;
26505     });
26506
26507     this.last = false;
26508     this.lastActive = false;
26509
26510     this.addEvents({
26511         /**
26512              * @event selectionchange
26513              * Fires when the selection changes
26514              * @param {SelectionModel} this
26515              */
26516             "selectionchange" : true,
26517         /**
26518              * @event afterselectionchange
26519              * Fires after the selection changes (eg. by key press or clicking)
26520              * @param {SelectionModel} this
26521              */
26522             "afterselectionchange" : true,
26523         /**
26524              * @event beforerowselect
26525              * Fires when a row is selected being selected, return false to cancel.
26526              * @param {SelectionModel} this
26527              * @param {Number} rowIndex The selected index
26528              * @param {Boolean} keepExisting False if other selections will be cleared
26529              */
26530             "beforerowselect" : true,
26531         /**
26532              * @event rowselect
26533              * Fires when a row is selected.
26534              * @param {SelectionModel} this
26535              * @param {Number} rowIndex The selected index
26536              * @param {Roo.data.Record} r The record
26537              */
26538             "rowselect" : true,
26539         /**
26540              * @event rowdeselect
26541              * Fires when a row is deselected.
26542              * @param {SelectionModel} this
26543              * @param {Number} rowIndex The selected index
26544              */
26545         "rowdeselect" : true
26546     });
26547     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26548     this.locked = false;
26549  };
26550
26551 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26552     /**
26553      * @cfg {Boolean} singleSelect
26554      * True to allow selection of only one row at a time (defaults to false)
26555      */
26556     singleSelect : false,
26557
26558     // private
26559     initEvents : function()
26560     {
26561
26562         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26563         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26564         //}else{ // allow click to work like normal
26565          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26566         //}
26567         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26568         this.grid.on("rowclick", this.handleMouseDown, this);
26569         
26570         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26571             "up" : function(e){
26572                 if(!e.shiftKey){
26573                     this.selectPrevious(e.shiftKey);
26574                 }else if(this.last !== false && this.lastActive !== false){
26575                     var last = this.last;
26576                     this.selectRange(this.last,  this.lastActive-1);
26577                     this.grid.getView().focusRow(this.lastActive);
26578                     if(last !== false){
26579                         this.last = last;
26580                     }
26581                 }else{
26582                     this.selectFirstRow();
26583                 }
26584                 this.fireEvent("afterselectionchange", this);
26585             },
26586             "down" : function(e){
26587                 if(!e.shiftKey){
26588                     this.selectNext(e.shiftKey);
26589                 }else if(this.last !== false && this.lastActive !== false){
26590                     var last = this.last;
26591                     this.selectRange(this.last,  this.lastActive+1);
26592                     this.grid.getView().focusRow(this.lastActive);
26593                     if(last !== false){
26594                         this.last = last;
26595                     }
26596                 }else{
26597                     this.selectFirstRow();
26598                 }
26599                 this.fireEvent("afterselectionchange", this);
26600             },
26601             scope: this
26602         });
26603         this.grid.store.on('load', function(){
26604             this.selections.clear();
26605         },this);
26606         /*
26607         var view = this.grid.view;
26608         view.on("refresh", this.onRefresh, this);
26609         view.on("rowupdated", this.onRowUpdated, this);
26610         view.on("rowremoved", this.onRemove, this);
26611         */
26612     },
26613
26614     // private
26615     onRefresh : function()
26616     {
26617         var ds = this.grid.store, i, v = this.grid.view;
26618         var s = this.selections;
26619         s.each(function(r){
26620             if((i = ds.indexOfId(r.id)) != -1){
26621                 v.onRowSelect(i);
26622             }else{
26623                 s.remove(r);
26624             }
26625         });
26626     },
26627
26628     // private
26629     onRemove : function(v, index, r){
26630         this.selections.remove(r);
26631     },
26632
26633     // private
26634     onRowUpdated : function(v, index, r){
26635         if(this.isSelected(r)){
26636             v.onRowSelect(index);
26637         }
26638     },
26639
26640     /**
26641      * Select records.
26642      * @param {Array} records The records to select
26643      * @param {Boolean} keepExisting (optional) True to keep existing selections
26644      */
26645     selectRecords : function(records, keepExisting)
26646     {
26647         if(!keepExisting){
26648             this.clearSelections();
26649         }
26650             var ds = this.grid.store;
26651         for(var i = 0, len = records.length; i < len; i++){
26652             this.selectRow(ds.indexOf(records[i]), true);
26653         }
26654     },
26655
26656     /**
26657      * Gets the number of selected rows.
26658      * @return {Number}
26659      */
26660     getCount : function(){
26661         return this.selections.length;
26662     },
26663
26664     /**
26665      * Selects the first row in the grid.
26666      */
26667     selectFirstRow : function(){
26668         this.selectRow(0);
26669     },
26670
26671     /**
26672      * Select the last row.
26673      * @param {Boolean} keepExisting (optional) True to keep existing selections
26674      */
26675     selectLastRow : function(keepExisting){
26676         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26677         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26678     },
26679
26680     /**
26681      * Selects the row immediately following the last selected row.
26682      * @param {Boolean} keepExisting (optional) True to keep existing selections
26683      */
26684     selectNext : function(keepExisting)
26685     {
26686             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26687             this.selectRow(this.last+1, keepExisting);
26688             this.grid.getView().focusRow(this.last);
26689         }
26690     },
26691
26692     /**
26693      * Selects the row that precedes the last selected row.
26694      * @param {Boolean} keepExisting (optional) True to keep existing selections
26695      */
26696     selectPrevious : function(keepExisting){
26697         if(this.last){
26698             this.selectRow(this.last-1, keepExisting);
26699             this.grid.getView().focusRow(this.last);
26700         }
26701     },
26702
26703     /**
26704      * Returns the selected records
26705      * @return {Array} Array of selected records
26706      */
26707     getSelections : function(){
26708         return [].concat(this.selections.items);
26709     },
26710
26711     /**
26712      * Returns the first selected record.
26713      * @return {Record}
26714      */
26715     getSelected : function(){
26716         return this.selections.itemAt(0);
26717     },
26718
26719
26720     /**
26721      * Clears all selections.
26722      */
26723     clearSelections : function(fast)
26724     {
26725         if(this.locked) {
26726             return;
26727         }
26728         if(fast !== true){
26729                 var ds = this.grid.store;
26730             var s = this.selections;
26731             s.each(function(r){
26732                 this.deselectRow(ds.indexOfId(r.id));
26733             }, this);
26734             s.clear();
26735         }else{
26736             this.selections.clear();
26737         }
26738         this.last = false;
26739     },
26740
26741
26742     /**
26743      * Selects all rows.
26744      */
26745     selectAll : function(){
26746         if(this.locked) {
26747             return;
26748         }
26749         this.selections.clear();
26750         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26751             this.selectRow(i, true);
26752         }
26753     },
26754
26755     /**
26756      * Returns True if there is a selection.
26757      * @return {Boolean}
26758      */
26759     hasSelection : function(){
26760         return this.selections.length > 0;
26761     },
26762
26763     /**
26764      * Returns True if the specified row is selected.
26765      * @param {Number/Record} record The record or index of the record to check
26766      * @return {Boolean}
26767      */
26768     isSelected : function(index){
26769             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26770         return (r && this.selections.key(r.id) ? true : false);
26771     },
26772
26773     /**
26774      * Returns True if the specified record id is selected.
26775      * @param {String} id The id of record to check
26776      * @return {Boolean}
26777      */
26778     isIdSelected : function(id){
26779         return (this.selections.key(id) ? true : false);
26780     },
26781
26782
26783     // private
26784     handleMouseDBClick : function(e, t){
26785         
26786     },
26787     // private
26788     handleMouseDown : function(e, t)
26789     {
26790             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26791         if(this.isLocked() || rowIndex < 0 ){
26792             return;
26793         };
26794         if(e.shiftKey && this.last !== false){
26795             var last = this.last;
26796             this.selectRange(last, rowIndex, e.ctrlKey);
26797             this.last = last; // reset the last
26798             t.focus();
26799     
26800         }else{
26801             var isSelected = this.isSelected(rowIndex);
26802             //Roo.log("select row:" + rowIndex);
26803             if(isSelected){
26804                 this.deselectRow(rowIndex);
26805             } else {
26806                         this.selectRow(rowIndex, true);
26807             }
26808     
26809             /*
26810                 if(e.button !== 0 && isSelected){
26811                 alert('rowIndex 2: ' + rowIndex);
26812                     view.focusRow(rowIndex);
26813                 }else if(e.ctrlKey && isSelected){
26814                     this.deselectRow(rowIndex);
26815                 }else if(!isSelected){
26816                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26817                     view.focusRow(rowIndex);
26818                 }
26819             */
26820         }
26821         this.fireEvent("afterselectionchange", this);
26822     },
26823     // private
26824     handleDragableRowClick :  function(grid, rowIndex, e) 
26825     {
26826         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26827             this.selectRow(rowIndex, false);
26828             grid.view.focusRow(rowIndex);
26829              this.fireEvent("afterselectionchange", this);
26830         }
26831     },
26832     
26833     /**
26834      * Selects multiple rows.
26835      * @param {Array} rows Array of the indexes of the row to select
26836      * @param {Boolean} keepExisting (optional) True to keep existing selections
26837      */
26838     selectRows : function(rows, keepExisting){
26839         if(!keepExisting){
26840             this.clearSelections();
26841         }
26842         for(var i = 0, len = rows.length; i < len; i++){
26843             this.selectRow(rows[i], true);
26844         }
26845     },
26846
26847     /**
26848      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26849      * @param {Number} startRow The index of the first row in the range
26850      * @param {Number} endRow The index of the last row in the range
26851      * @param {Boolean} keepExisting (optional) True to retain existing selections
26852      */
26853     selectRange : function(startRow, endRow, keepExisting){
26854         if(this.locked) {
26855             return;
26856         }
26857         if(!keepExisting){
26858             this.clearSelections();
26859         }
26860         if(startRow <= endRow){
26861             for(var i = startRow; i <= endRow; i++){
26862                 this.selectRow(i, true);
26863             }
26864         }else{
26865             for(var i = startRow; i >= endRow; i--){
26866                 this.selectRow(i, true);
26867             }
26868         }
26869     },
26870
26871     /**
26872      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26873      * @param {Number} startRow The index of the first row in the range
26874      * @param {Number} endRow The index of the last row in the range
26875      */
26876     deselectRange : function(startRow, endRow, preventViewNotify){
26877         if(this.locked) {
26878             return;
26879         }
26880         for(var i = startRow; i <= endRow; i++){
26881             this.deselectRow(i, preventViewNotify);
26882         }
26883     },
26884
26885     /**
26886      * Selects a row.
26887      * @param {Number} row The index of the row to select
26888      * @param {Boolean} keepExisting (optional) True to keep existing selections
26889      */
26890     selectRow : function(index, keepExisting, preventViewNotify)
26891     {
26892             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26893             return;
26894         }
26895         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26896             if(!keepExisting || this.singleSelect){
26897                 this.clearSelections();
26898             }
26899             
26900             var r = this.grid.store.getAt(index);
26901             //console.log('selectRow - record id :' + r.id);
26902             
26903             this.selections.add(r);
26904             this.last = this.lastActive = index;
26905             if(!preventViewNotify){
26906                 var proxy = new Roo.Element(
26907                                 this.grid.getRowDom(index)
26908                 );
26909                 proxy.addClass('bg-info info');
26910             }
26911             this.fireEvent("rowselect", this, index, r);
26912             this.fireEvent("selectionchange", this);
26913         }
26914     },
26915
26916     /**
26917      * Deselects a row.
26918      * @param {Number} row The index of the row to deselect
26919      */
26920     deselectRow : function(index, preventViewNotify)
26921     {
26922         if(this.locked) {
26923             return;
26924         }
26925         if(this.last == index){
26926             this.last = false;
26927         }
26928         if(this.lastActive == index){
26929             this.lastActive = false;
26930         }
26931         
26932         var r = this.grid.store.getAt(index);
26933         if (!r) {
26934             return;
26935         }
26936         
26937         this.selections.remove(r);
26938         //.console.log('deselectRow - record id :' + r.id);
26939         if(!preventViewNotify){
26940         
26941             var proxy = new Roo.Element(
26942                 this.grid.getRowDom(index)
26943             );
26944             proxy.removeClass('bg-info info');
26945         }
26946         this.fireEvent("rowdeselect", this, index);
26947         this.fireEvent("selectionchange", this);
26948     },
26949
26950     // private
26951     restoreLast : function(){
26952         if(this._last){
26953             this.last = this._last;
26954         }
26955     },
26956
26957     // private
26958     acceptsNav : function(row, col, cm){
26959         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26960     },
26961
26962     // private
26963     onEditorKey : function(field, e){
26964         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26965         if(k == e.TAB){
26966             e.stopEvent();
26967             ed.completeEdit();
26968             if(e.shiftKey){
26969                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26970             }else{
26971                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26972             }
26973         }else if(k == e.ENTER && !e.ctrlKey){
26974             e.stopEvent();
26975             ed.completeEdit();
26976             if(e.shiftKey){
26977                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26978             }else{
26979                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26980             }
26981         }else if(k == e.ESC){
26982             ed.cancelEdit();
26983         }
26984         if(newCell){
26985             g.startEditing(newCell[0], newCell[1]);
26986         }
26987     }
26988 });
26989 /*
26990  * Based on:
26991  * Ext JS Library 1.1.1
26992  * Copyright(c) 2006-2007, Ext JS, LLC.
26993  *
26994  * Originally Released Under LGPL - original licence link has changed is not relivant.
26995  *
26996  * Fork - LGPL
26997  * <script type="text/javascript">
26998  */
26999  
27000 /**
27001  * @class Roo.bootstrap.PagingToolbar
27002  * @extends Roo.bootstrap.NavSimplebar
27003  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27004  * @constructor
27005  * Create a new PagingToolbar
27006  * @param {Object} config The config object
27007  * @param {Roo.data.Store} store
27008  */
27009 Roo.bootstrap.PagingToolbar = function(config)
27010 {
27011     // old args format still supported... - xtype is prefered..
27012         // created from xtype...
27013     
27014     this.ds = config.dataSource;
27015     
27016     if (config.store && !this.ds) {
27017         this.store= Roo.factory(config.store, Roo.data);
27018         this.ds = this.store;
27019         this.ds.xmodule = this.xmodule || false;
27020     }
27021     
27022     this.toolbarItems = [];
27023     if (config.items) {
27024         this.toolbarItems = config.items;
27025     }
27026     
27027     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27028     
27029     this.cursor = 0;
27030     
27031     if (this.ds) { 
27032         this.bind(this.ds);
27033     }
27034     
27035     if (Roo.bootstrap.version == 4) {
27036         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27037     } else {
27038         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27039     }
27040     
27041 };
27042
27043 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27044     /**
27045      * @cfg {Roo.data.Store} dataSource
27046      * The underlying data store providing the paged data
27047      */
27048     /**
27049      * @cfg {String/HTMLElement/Element} container
27050      * container The id or element that will contain the toolbar
27051      */
27052     /**
27053      * @cfg {Boolean} displayInfo
27054      * True to display the displayMsg (defaults to false)
27055      */
27056     /**
27057      * @cfg {Number} pageSize
27058      * The number of records to display per page (defaults to 20)
27059      */
27060     pageSize: 20,
27061     /**
27062      * @cfg {String} displayMsg
27063      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27064      */
27065     displayMsg : 'Displaying {0} - {1} of {2}',
27066     /**
27067      * @cfg {String} emptyMsg
27068      * The message to display when no records are found (defaults to "No data to display")
27069      */
27070     emptyMsg : 'No data to display',
27071     /**
27072      * Customizable piece of the default paging text (defaults to "Page")
27073      * @type String
27074      */
27075     beforePageText : "Page",
27076     /**
27077      * Customizable piece of the default paging text (defaults to "of %0")
27078      * @type String
27079      */
27080     afterPageText : "of {0}",
27081     /**
27082      * Customizable piece of the default paging text (defaults to "First Page")
27083      * @type String
27084      */
27085     firstText : "First Page",
27086     /**
27087      * Customizable piece of the default paging text (defaults to "Previous Page")
27088      * @type String
27089      */
27090     prevText : "Previous Page",
27091     /**
27092      * Customizable piece of the default paging text (defaults to "Next Page")
27093      * @type String
27094      */
27095     nextText : "Next Page",
27096     /**
27097      * Customizable piece of the default paging text (defaults to "Last Page")
27098      * @type String
27099      */
27100     lastText : "Last Page",
27101     /**
27102      * Customizable piece of the default paging text (defaults to "Refresh")
27103      * @type String
27104      */
27105     refreshText : "Refresh",
27106
27107     buttons : false,
27108     // private
27109     onRender : function(ct, position) 
27110     {
27111         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27112         this.navgroup.parentId = this.id;
27113         this.navgroup.onRender(this.el, null);
27114         // add the buttons to the navgroup
27115         
27116         if(this.displayInfo){
27117             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27118             this.displayEl = this.el.select('.x-paging-info', true).first();
27119 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27120 //            this.displayEl = navel.el.select('span',true).first();
27121         }
27122         
27123         var _this = this;
27124         
27125         if(this.buttons){
27126             Roo.each(_this.buttons, function(e){ // this might need to use render????
27127                Roo.factory(e).render(_this.el);
27128             });
27129         }
27130             
27131         Roo.each(_this.toolbarItems, function(e) {
27132             _this.navgroup.addItem(e);
27133         });
27134         
27135         
27136         this.first = this.navgroup.addItem({
27137             tooltip: this.firstText,
27138             cls: "prev btn-outline-secondary",
27139             html : ' <i class="fa fa-step-backward"></i>',
27140             disabled: true,
27141             preventDefault: true,
27142             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27143         });
27144         
27145         this.prev =  this.navgroup.addItem({
27146             tooltip: this.prevText,
27147             cls: "prev btn-outline-secondary",
27148             html : ' <i class="fa fa-backward"></i>',
27149             disabled: true,
27150             preventDefault: true,
27151             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27152         });
27153     //this.addSeparator();
27154         
27155         
27156         var field = this.navgroup.addItem( {
27157             tagtype : 'span',
27158             cls : 'x-paging-position  btn-outline-secondary',
27159              disabled: true,
27160             html : this.beforePageText  +
27161                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27162                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27163          } ); //?? escaped?
27164         
27165         this.field = field.el.select('input', true).first();
27166         this.field.on("keydown", this.onPagingKeydown, this);
27167         this.field.on("focus", function(){this.dom.select();});
27168     
27169     
27170         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27171         //this.field.setHeight(18);
27172         //this.addSeparator();
27173         this.next = this.navgroup.addItem({
27174             tooltip: this.nextText,
27175             cls: "next btn-outline-secondary",
27176             html : ' <i class="fa fa-forward"></i>',
27177             disabled: true,
27178             preventDefault: true,
27179             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27180         });
27181         this.last = this.navgroup.addItem({
27182             tooltip: this.lastText,
27183             html : ' <i class="fa fa-step-forward"></i>',
27184             cls: "next btn-outline-secondary",
27185             disabled: true,
27186             preventDefault: true,
27187             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27188         });
27189     //this.addSeparator();
27190         this.loading = this.navgroup.addItem({
27191             tooltip: this.refreshText,
27192             cls: "btn-outline-secondary",
27193             html : ' <i class="fa fa-refresh"></i>',
27194             preventDefault: true,
27195             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27196         });
27197         
27198     },
27199
27200     // private
27201     updateInfo : function(){
27202         if(this.displayEl){
27203             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27204             var msg = count == 0 ?
27205                 this.emptyMsg :
27206                 String.format(
27207                     this.displayMsg,
27208                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27209                 );
27210             this.displayEl.update(msg);
27211         }
27212     },
27213
27214     // private
27215     onLoad : function(ds, r, o)
27216     {
27217         this.cursor = o.params.start ? o.params.start : 0;
27218         
27219         var d = this.getPageData(),
27220             ap = d.activePage,
27221             ps = d.pages;
27222         
27223         
27224         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27225         this.field.dom.value = ap;
27226         this.first.setDisabled(ap == 1);
27227         this.prev.setDisabled(ap == 1);
27228         this.next.setDisabled(ap == ps);
27229         this.last.setDisabled(ap == ps);
27230         this.loading.enable();
27231         this.updateInfo();
27232     },
27233
27234     // private
27235     getPageData : function(){
27236         var total = this.ds.getTotalCount();
27237         return {
27238             total : total,
27239             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27240             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27241         };
27242     },
27243
27244     // private
27245     onLoadError : function(){
27246         this.loading.enable();
27247     },
27248
27249     // private
27250     onPagingKeydown : function(e){
27251         var k = e.getKey();
27252         var d = this.getPageData();
27253         if(k == e.RETURN){
27254             var v = this.field.dom.value, pageNum;
27255             if(!v || isNaN(pageNum = parseInt(v, 10))){
27256                 this.field.dom.value = d.activePage;
27257                 return;
27258             }
27259             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27260             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27261             e.stopEvent();
27262         }
27263         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))
27264         {
27265           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27266           this.field.dom.value = pageNum;
27267           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27268           e.stopEvent();
27269         }
27270         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27271         {
27272           var v = this.field.dom.value, pageNum; 
27273           var increment = (e.shiftKey) ? 10 : 1;
27274           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27275                 increment *= -1;
27276           }
27277           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27278             this.field.dom.value = d.activePage;
27279             return;
27280           }
27281           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27282           {
27283             this.field.dom.value = parseInt(v, 10) + increment;
27284             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27285             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27286           }
27287           e.stopEvent();
27288         }
27289     },
27290
27291     // private
27292     beforeLoad : function(){
27293         if(this.loading){
27294             this.loading.disable();
27295         }
27296     },
27297
27298     // private
27299     onClick : function(which){
27300         
27301         var ds = this.ds;
27302         if (!ds) {
27303             return;
27304         }
27305         
27306         switch(which){
27307             case "first":
27308                 ds.load({params:{start: 0, limit: this.pageSize}});
27309             break;
27310             case "prev":
27311                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27312             break;
27313             case "next":
27314                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27315             break;
27316             case "last":
27317                 var total = ds.getTotalCount();
27318                 var extra = total % this.pageSize;
27319                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27320                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27321             break;
27322             case "refresh":
27323                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27324             break;
27325         }
27326     },
27327
27328     /**
27329      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27330      * @param {Roo.data.Store} store The data store to unbind
27331      */
27332     unbind : function(ds){
27333         ds.un("beforeload", this.beforeLoad, this);
27334         ds.un("load", this.onLoad, this);
27335         ds.un("loadexception", this.onLoadError, this);
27336         ds.un("remove", this.updateInfo, this);
27337         ds.un("add", this.updateInfo, this);
27338         this.ds = undefined;
27339     },
27340
27341     /**
27342      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27343      * @param {Roo.data.Store} store The data store to bind
27344      */
27345     bind : function(ds){
27346         ds.on("beforeload", this.beforeLoad, this);
27347         ds.on("load", this.onLoad, this);
27348         ds.on("loadexception", this.onLoadError, this);
27349         ds.on("remove", this.updateInfo, this);
27350         ds.on("add", this.updateInfo, this);
27351         this.ds = ds;
27352     }
27353 });/*
27354  * - LGPL
27355  *
27356  * element
27357  * 
27358  */
27359
27360 /**
27361  * @class Roo.bootstrap.MessageBar
27362  * @extends Roo.bootstrap.Component
27363  * Bootstrap MessageBar class
27364  * @cfg {String} html contents of the MessageBar
27365  * @cfg {String} weight (info | success | warning | danger) default info
27366  * @cfg {String} beforeClass insert the bar before the given class
27367  * @cfg {Boolean} closable (true | false) default false
27368  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27369  * 
27370  * @constructor
27371  * Create a new Element
27372  * @param {Object} config The config object
27373  */
27374
27375 Roo.bootstrap.MessageBar = function(config){
27376     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27377 };
27378
27379 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27380     
27381     html: '',
27382     weight: 'info',
27383     closable: false,
27384     fixed: false,
27385     beforeClass: 'bootstrap-sticky-wrap',
27386     
27387     getAutoCreate : function(){
27388         
27389         var cfg = {
27390             tag: 'div',
27391             cls: 'alert alert-dismissable alert-' + this.weight,
27392             cn: [
27393                 {
27394                     tag: 'span',
27395                     cls: 'message',
27396                     html: this.html || ''
27397                 }
27398             ]
27399         };
27400         
27401         if(this.fixed){
27402             cfg.cls += ' alert-messages-fixed';
27403         }
27404         
27405         if(this.closable){
27406             cfg.cn.push({
27407                 tag: 'button',
27408                 cls: 'close',
27409                 html: 'x'
27410             });
27411         }
27412         
27413         return cfg;
27414     },
27415     
27416     onRender : function(ct, position)
27417     {
27418         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27419         
27420         if(!this.el){
27421             var cfg = Roo.apply({},  this.getAutoCreate());
27422             cfg.id = Roo.id();
27423             
27424             if (this.cls) {
27425                 cfg.cls += ' ' + this.cls;
27426             }
27427             if (this.style) {
27428                 cfg.style = this.style;
27429             }
27430             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27431             
27432             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27433         }
27434         
27435         this.el.select('>button.close').on('click', this.hide, this);
27436         
27437     },
27438     
27439     show : function()
27440     {
27441         if (!this.rendered) {
27442             this.render();
27443         }
27444         
27445         this.el.show();
27446         
27447         this.fireEvent('show', this);
27448         
27449     },
27450     
27451     hide : function()
27452     {
27453         if (!this.rendered) {
27454             this.render();
27455         }
27456         
27457         this.el.hide();
27458         
27459         this.fireEvent('hide', this);
27460     },
27461     
27462     update : function()
27463     {
27464 //        var e = this.el.dom.firstChild;
27465 //        
27466 //        if(this.closable){
27467 //            e = e.nextSibling;
27468 //        }
27469 //        
27470 //        e.data = this.html || '';
27471
27472         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27473     }
27474    
27475 });
27476
27477  
27478
27479      /*
27480  * - LGPL
27481  *
27482  * Graph
27483  * 
27484  */
27485
27486
27487 /**
27488  * @class Roo.bootstrap.Graph
27489  * @extends Roo.bootstrap.Component
27490  * Bootstrap Graph class
27491 > Prameters
27492  -sm {number} sm 4
27493  -md {number} md 5
27494  @cfg {String} graphtype  bar | vbar | pie
27495  @cfg {number} g_x coodinator | centre x (pie)
27496  @cfg {number} g_y coodinator | centre y (pie)
27497  @cfg {number} g_r radius (pie)
27498  @cfg {number} g_height height of the chart (respected by all elements in the set)
27499  @cfg {number} g_width width of the chart (respected by all elements in the set)
27500  @cfg {Object} title The title of the chart
27501     
27502  -{Array}  values
27503  -opts (object) options for the chart 
27504      o {
27505      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27506      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27507      o vgutter (number)
27508      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.
27509      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27510      o to
27511      o stretch (boolean)
27512      o }
27513  -opts (object) options for the pie
27514      o{
27515      o cut
27516      o startAngle (number)
27517      o endAngle (number)
27518      } 
27519  *
27520  * @constructor
27521  * Create a new Input
27522  * @param {Object} config The config object
27523  */
27524
27525 Roo.bootstrap.Graph = function(config){
27526     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27527     
27528     this.addEvents({
27529         // img events
27530         /**
27531          * @event click
27532          * The img click event for the img.
27533          * @param {Roo.EventObject} e
27534          */
27535         "click" : true
27536     });
27537 };
27538
27539 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27540     
27541     sm: 4,
27542     md: 5,
27543     graphtype: 'bar',
27544     g_height: 250,
27545     g_width: 400,
27546     g_x: 50,
27547     g_y: 50,
27548     g_r: 30,
27549     opts:{
27550         //g_colors: this.colors,
27551         g_type: 'soft',
27552         g_gutter: '20%'
27553
27554     },
27555     title : false,
27556
27557     getAutoCreate : function(){
27558         
27559         var cfg = {
27560             tag: 'div',
27561             html : null
27562         };
27563         
27564         
27565         return  cfg;
27566     },
27567
27568     onRender : function(ct,position){
27569         
27570         
27571         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27572         
27573         if (typeof(Raphael) == 'undefined') {
27574             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27575             return;
27576         }
27577         
27578         this.raphael = Raphael(this.el.dom);
27579         
27580                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27581                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27582                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27583                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27584                 /*
27585                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27586                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27587                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27588                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27589                 
27590                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27591                 r.barchart(330, 10, 300, 220, data1);
27592                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27593                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27594                 */
27595                 
27596                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27597                 // r.barchart(30, 30, 560, 250,  xdata, {
27598                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27599                 //     axis : "0 0 1 1",
27600                 //     axisxlabels :  xdata
27601                 //     //yvalues : cols,
27602                    
27603                 // });
27604 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27605 //        
27606 //        this.load(null,xdata,{
27607 //                axis : "0 0 1 1",
27608 //                axisxlabels :  xdata
27609 //                });
27610
27611     },
27612
27613     load : function(graphtype,xdata,opts)
27614     {
27615         this.raphael.clear();
27616         if(!graphtype) {
27617             graphtype = this.graphtype;
27618         }
27619         if(!opts){
27620             opts = this.opts;
27621         }
27622         var r = this.raphael,
27623             fin = function () {
27624                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27625             },
27626             fout = function () {
27627                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27628             },
27629             pfin = function() {
27630                 this.sector.stop();
27631                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27632
27633                 if (this.label) {
27634                     this.label[0].stop();
27635                     this.label[0].attr({ r: 7.5 });
27636                     this.label[1].attr({ "font-weight": 800 });
27637                 }
27638             },
27639             pfout = function() {
27640                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27641
27642                 if (this.label) {
27643                     this.label[0].animate({ r: 5 }, 500, "bounce");
27644                     this.label[1].attr({ "font-weight": 400 });
27645                 }
27646             };
27647
27648         switch(graphtype){
27649             case 'bar':
27650                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27651                 break;
27652             case 'hbar':
27653                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27654                 break;
27655             case 'pie':
27656 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27657 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27658 //            
27659                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27660                 
27661                 break;
27662
27663         }
27664         
27665         if(this.title){
27666             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27667         }
27668         
27669     },
27670     
27671     setTitle: function(o)
27672     {
27673         this.title = o;
27674     },
27675     
27676     initEvents: function() {
27677         
27678         if(!this.href){
27679             this.el.on('click', this.onClick, this);
27680         }
27681     },
27682     
27683     onClick : function(e)
27684     {
27685         Roo.log('img onclick');
27686         this.fireEvent('click', this, e);
27687     }
27688    
27689 });
27690
27691  
27692 /*
27693  * - LGPL
27694  *
27695  * numberBox
27696  * 
27697  */
27698 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27699
27700 /**
27701  * @class Roo.bootstrap.dash.NumberBox
27702  * @extends Roo.bootstrap.Component
27703  * Bootstrap NumberBox class
27704  * @cfg {String} headline Box headline
27705  * @cfg {String} content Box content
27706  * @cfg {String} icon Box icon
27707  * @cfg {String} footer Footer text
27708  * @cfg {String} fhref Footer href
27709  * 
27710  * @constructor
27711  * Create a new NumberBox
27712  * @param {Object} config The config object
27713  */
27714
27715
27716 Roo.bootstrap.dash.NumberBox = function(config){
27717     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27718     
27719 };
27720
27721 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27722     
27723     headline : '',
27724     content : '',
27725     icon : '',
27726     footer : '',
27727     fhref : '',
27728     ficon : '',
27729     
27730     getAutoCreate : function(){
27731         
27732         var cfg = {
27733             tag : 'div',
27734             cls : 'small-box ',
27735             cn : [
27736                 {
27737                     tag : 'div',
27738                     cls : 'inner',
27739                     cn :[
27740                         {
27741                             tag : 'h3',
27742                             cls : 'roo-headline',
27743                             html : this.headline
27744                         },
27745                         {
27746                             tag : 'p',
27747                             cls : 'roo-content',
27748                             html : this.content
27749                         }
27750                     ]
27751                 }
27752             ]
27753         };
27754         
27755         if(this.icon){
27756             cfg.cn.push({
27757                 tag : 'div',
27758                 cls : 'icon',
27759                 cn :[
27760                     {
27761                         tag : 'i',
27762                         cls : 'ion ' + this.icon
27763                     }
27764                 ]
27765             });
27766         }
27767         
27768         if(this.footer){
27769             var footer = {
27770                 tag : 'a',
27771                 cls : 'small-box-footer',
27772                 href : this.fhref || '#',
27773                 html : this.footer
27774             };
27775             
27776             cfg.cn.push(footer);
27777             
27778         }
27779         
27780         return  cfg;
27781     },
27782
27783     onRender : function(ct,position){
27784         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27785
27786
27787        
27788                 
27789     },
27790
27791     setHeadline: function (value)
27792     {
27793         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27794     },
27795     
27796     setFooter: function (value, href)
27797     {
27798         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27799         
27800         if(href){
27801             this.el.select('a.small-box-footer',true).first().attr('href', href);
27802         }
27803         
27804     },
27805
27806     setContent: function (value)
27807     {
27808         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27809     },
27810
27811     initEvents: function() 
27812     {   
27813         
27814     }
27815     
27816 });
27817
27818  
27819 /*
27820  * - LGPL
27821  *
27822  * TabBox
27823  * 
27824  */
27825 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27826
27827 /**
27828  * @class Roo.bootstrap.dash.TabBox
27829  * @extends Roo.bootstrap.Component
27830  * Bootstrap TabBox class
27831  * @cfg {String} title Title of the TabBox
27832  * @cfg {String} icon Icon of the TabBox
27833  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27834  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27835  * 
27836  * @constructor
27837  * Create a new TabBox
27838  * @param {Object} config The config object
27839  */
27840
27841
27842 Roo.bootstrap.dash.TabBox = function(config){
27843     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27844     this.addEvents({
27845         // raw events
27846         /**
27847          * @event addpane
27848          * When a pane is added
27849          * @param {Roo.bootstrap.dash.TabPane} pane
27850          */
27851         "addpane" : true,
27852         /**
27853          * @event activatepane
27854          * When a pane is activated
27855          * @param {Roo.bootstrap.dash.TabPane} pane
27856          */
27857         "activatepane" : true
27858         
27859          
27860     });
27861     
27862     this.panes = [];
27863 };
27864
27865 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27866
27867     title : '',
27868     icon : false,
27869     showtabs : true,
27870     tabScrollable : false,
27871     
27872     getChildContainer : function()
27873     {
27874         return this.el.select('.tab-content', true).first();
27875     },
27876     
27877     getAutoCreate : function(){
27878         
27879         var header = {
27880             tag: 'li',
27881             cls: 'pull-left header',
27882             html: this.title,
27883             cn : []
27884         };
27885         
27886         if(this.icon){
27887             header.cn.push({
27888                 tag: 'i',
27889                 cls: 'fa ' + this.icon
27890             });
27891         }
27892         
27893         var h = {
27894             tag: 'ul',
27895             cls: 'nav nav-tabs pull-right',
27896             cn: [
27897                 header
27898             ]
27899         };
27900         
27901         if(this.tabScrollable){
27902             h = {
27903                 tag: 'div',
27904                 cls: 'tab-header',
27905                 cn: [
27906                     {
27907                         tag: 'ul',
27908                         cls: 'nav nav-tabs pull-right',
27909                         cn: [
27910                             header
27911                         ]
27912                     }
27913                 ]
27914             };
27915         }
27916         
27917         var cfg = {
27918             tag: 'div',
27919             cls: 'nav-tabs-custom',
27920             cn: [
27921                 h,
27922                 {
27923                     tag: 'div',
27924                     cls: 'tab-content no-padding',
27925                     cn: []
27926                 }
27927             ]
27928         };
27929
27930         return  cfg;
27931     },
27932     initEvents : function()
27933     {
27934         //Roo.log('add add pane handler');
27935         this.on('addpane', this.onAddPane, this);
27936     },
27937      /**
27938      * Updates the box title
27939      * @param {String} html to set the title to.
27940      */
27941     setTitle : function(value)
27942     {
27943         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27944     },
27945     onAddPane : function(pane)
27946     {
27947         this.panes.push(pane);
27948         //Roo.log('addpane');
27949         //Roo.log(pane);
27950         // tabs are rendere left to right..
27951         if(!this.showtabs){
27952             return;
27953         }
27954         
27955         var ctr = this.el.select('.nav-tabs', true).first();
27956          
27957          
27958         var existing = ctr.select('.nav-tab',true);
27959         var qty = existing.getCount();;
27960         
27961         
27962         var tab = ctr.createChild({
27963             tag : 'li',
27964             cls : 'nav-tab' + (qty ? '' : ' active'),
27965             cn : [
27966                 {
27967                     tag : 'a',
27968                     href:'#',
27969                     html : pane.title
27970                 }
27971             ]
27972         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27973         pane.tab = tab;
27974         
27975         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27976         if (!qty) {
27977             pane.el.addClass('active');
27978         }
27979         
27980                 
27981     },
27982     onTabClick : function(ev,un,ob,pane)
27983     {
27984         //Roo.log('tab - prev default');
27985         ev.preventDefault();
27986         
27987         
27988         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27989         pane.tab.addClass('active');
27990         //Roo.log(pane.title);
27991         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27992         // technically we should have a deactivate event.. but maybe add later.
27993         // and it should not de-activate the selected tab...
27994         this.fireEvent('activatepane', pane);
27995         pane.el.addClass('active');
27996         pane.fireEvent('activate');
27997         
27998         
27999     },
28000     
28001     getActivePane : function()
28002     {
28003         var r = false;
28004         Roo.each(this.panes, function(p) {
28005             if(p.el.hasClass('active')){
28006                 r = p;
28007                 return false;
28008             }
28009             
28010             return;
28011         });
28012         
28013         return r;
28014     }
28015     
28016     
28017 });
28018
28019  
28020 /*
28021  * - LGPL
28022  *
28023  * Tab pane
28024  * 
28025  */
28026 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28027 /**
28028  * @class Roo.bootstrap.TabPane
28029  * @extends Roo.bootstrap.Component
28030  * Bootstrap TabPane class
28031  * @cfg {Boolean} active (false | true) Default false
28032  * @cfg {String} title title of panel
28033
28034  * 
28035  * @constructor
28036  * Create a new TabPane
28037  * @param {Object} config The config object
28038  */
28039
28040 Roo.bootstrap.dash.TabPane = function(config){
28041     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28042     
28043     this.addEvents({
28044         // raw events
28045         /**
28046          * @event activate
28047          * When a pane is activated
28048          * @param {Roo.bootstrap.dash.TabPane} pane
28049          */
28050         "activate" : true
28051          
28052     });
28053 };
28054
28055 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28056     
28057     active : false,
28058     title : '',
28059     
28060     // the tabBox that this is attached to.
28061     tab : false,
28062      
28063     getAutoCreate : function() 
28064     {
28065         var cfg = {
28066             tag: 'div',
28067             cls: 'tab-pane'
28068         };
28069         
28070         if(this.active){
28071             cfg.cls += ' active';
28072         }
28073         
28074         return cfg;
28075     },
28076     initEvents  : function()
28077     {
28078         //Roo.log('trigger add pane handler');
28079         this.parent().fireEvent('addpane', this)
28080     },
28081     
28082      /**
28083      * Updates the tab title 
28084      * @param {String} html to set the title to.
28085      */
28086     setTitle: function(str)
28087     {
28088         if (!this.tab) {
28089             return;
28090         }
28091         this.title = str;
28092         this.tab.select('a', true).first().dom.innerHTML = str;
28093         
28094     }
28095     
28096     
28097     
28098 });
28099
28100  
28101
28102
28103  /*
28104  * - LGPL
28105  *
28106  * menu
28107  * 
28108  */
28109 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28110
28111 /**
28112  * @class Roo.bootstrap.menu.Menu
28113  * @extends Roo.bootstrap.Component
28114  * Bootstrap Menu class - container for Menu
28115  * @cfg {String} html Text of the menu
28116  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28117  * @cfg {String} icon Font awesome icon
28118  * @cfg {String} pos Menu align to (top | bottom) default bottom
28119  * 
28120  * 
28121  * @constructor
28122  * Create a new Menu
28123  * @param {Object} config The config object
28124  */
28125
28126
28127 Roo.bootstrap.menu.Menu = function(config){
28128     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28129     
28130     this.addEvents({
28131         /**
28132          * @event beforeshow
28133          * Fires before this menu is displayed
28134          * @param {Roo.bootstrap.menu.Menu} this
28135          */
28136         beforeshow : true,
28137         /**
28138          * @event beforehide
28139          * Fires before this menu is hidden
28140          * @param {Roo.bootstrap.menu.Menu} this
28141          */
28142         beforehide : true,
28143         /**
28144          * @event show
28145          * Fires after this menu is displayed
28146          * @param {Roo.bootstrap.menu.Menu} this
28147          */
28148         show : true,
28149         /**
28150          * @event hide
28151          * Fires after this menu is hidden
28152          * @param {Roo.bootstrap.menu.Menu} this
28153          */
28154         hide : true,
28155         /**
28156          * @event click
28157          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28158          * @param {Roo.bootstrap.menu.Menu} this
28159          * @param {Roo.EventObject} e
28160          */
28161         click : true
28162     });
28163     
28164 };
28165
28166 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28167     
28168     submenu : false,
28169     html : '',
28170     weight : 'default',
28171     icon : false,
28172     pos : 'bottom',
28173     
28174     
28175     getChildContainer : function() {
28176         if(this.isSubMenu){
28177             return this.el;
28178         }
28179         
28180         return this.el.select('ul.dropdown-menu', true).first();  
28181     },
28182     
28183     getAutoCreate : function()
28184     {
28185         var text = [
28186             {
28187                 tag : 'span',
28188                 cls : 'roo-menu-text',
28189                 html : this.html
28190             }
28191         ];
28192         
28193         if(this.icon){
28194             text.unshift({
28195                 tag : 'i',
28196                 cls : 'fa ' + this.icon
28197             })
28198         }
28199         
28200         
28201         var cfg = {
28202             tag : 'div',
28203             cls : 'btn-group',
28204             cn : [
28205                 {
28206                     tag : 'button',
28207                     cls : 'dropdown-button btn btn-' + this.weight,
28208                     cn : text
28209                 },
28210                 {
28211                     tag : 'button',
28212                     cls : 'dropdown-toggle btn btn-' + this.weight,
28213                     cn : [
28214                         {
28215                             tag : 'span',
28216                             cls : 'caret'
28217                         }
28218                     ]
28219                 },
28220                 {
28221                     tag : 'ul',
28222                     cls : 'dropdown-menu'
28223                 }
28224             ]
28225             
28226         };
28227         
28228         if(this.pos == 'top'){
28229             cfg.cls += ' dropup';
28230         }
28231         
28232         if(this.isSubMenu){
28233             cfg = {
28234                 tag : 'ul',
28235                 cls : 'dropdown-menu'
28236             }
28237         }
28238         
28239         return cfg;
28240     },
28241     
28242     onRender : function(ct, position)
28243     {
28244         this.isSubMenu = ct.hasClass('dropdown-submenu');
28245         
28246         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28247     },
28248     
28249     initEvents : function() 
28250     {
28251         if(this.isSubMenu){
28252             return;
28253         }
28254         
28255         this.hidden = true;
28256         
28257         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28258         this.triggerEl.on('click', this.onTriggerPress, this);
28259         
28260         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28261         this.buttonEl.on('click', this.onClick, this);
28262         
28263     },
28264     
28265     list : function()
28266     {
28267         if(this.isSubMenu){
28268             return this.el;
28269         }
28270         
28271         return this.el.select('ul.dropdown-menu', true).first();
28272     },
28273     
28274     onClick : function(e)
28275     {
28276         this.fireEvent("click", this, e);
28277     },
28278     
28279     onTriggerPress  : function(e)
28280     {   
28281         if (this.isVisible()) {
28282             this.hide();
28283         } else {
28284             this.show();
28285         }
28286     },
28287     
28288     isVisible : function(){
28289         return !this.hidden;
28290     },
28291     
28292     show : function()
28293     {
28294         this.fireEvent("beforeshow", this);
28295         
28296         this.hidden = false;
28297         this.el.addClass('open');
28298         
28299         Roo.get(document).on("mouseup", this.onMouseUp, this);
28300         
28301         this.fireEvent("show", this);
28302         
28303         
28304     },
28305     
28306     hide : function()
28307     {
28308         this.fireEvent("beforehide", this);
28309         
28310         this.hidden = true;
28311         this.el.removeClass('open');
28312         
28313         Roo.get(document).un("mouseup", this.onMouseUp);
28314         
28315         this.fireEvent("hide", this);
28316     },
28317     
28318     onMouseUp : function()
28319     {
28320         this.hide();
28321     }
28322     
28323 });
28324
28325  
28326  /*
28327  * - LGPL
28328  *
28329  * menu item
28330  * 
28331  */
28332 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28333
28334 /**
28335  * @class Roo.bootstrap.menu.Item
28336  * @extends Roo.bootstrap.Component
28337  * Bootstrap MenuItem class
28338  * @cfg {Boolean} submenu (true | false) default false
28339  * @cfg {String} html text of the item
28340  * @cfg {String} href the link
28341  * @cfg {Boolean} disable (true | false) default false
28342  * @cfg {Boolean} preventDefault (true | false) default true
28343  * @cfg {String} icon Font awesome icon
28344  * @cfg {String} pos Submenu align to (left | right) default right 
28345  * 
28346  * 
28347  * @constructor
28348  * Create a new Item
28349  * @param {Object} config The config object
28350  */
28351
28352
28353 Roo.bootstrap.menu.Item = function(config){
28354     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28355     this.addEvents({
28356         /**
28357          * @event mouseover
28358          * Fires when the mouse is hovering over this menu
28359          * @param {Roo.bootstrap.menu.Item} this
28360          * @param {Roo.EventObject} e
28361          */
28362         mouseover : true,
28363         /**
28364          * @event mouseout
28365          * Fires when the mouse exits this menu
28366          * @param {Roo.bootstrap.menu.Item} this
28367          * @param {Roo.EventObject} e
28368          */
28369         mouseout : true,
28370         // raw events
28371         /**
28372          * @event click
28373          * The raw click event for the entire grid.
28374          * @param {Roo.EventObject} e
28375          */
28376         click : true
28377     });
28378 };
28379
28380 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28381     
28382     submenu : false,
28383     href : '',
28384     html : '',
28385     preventDefault: true,
28386     disable : false,
28387     icon : false,
28388     pos : 'right',
28389     
28390     getAutoCreate : function()
28391     {
28392         var text = [
28393             {
28394                 tag : 'span',
28395                 cls : 'roo-menu-item-text',
28396                 html : this.html
28397             }
28398         ];
28399         
28400         if(this.icon){
28401             text.unshift({
28402                 tag : 'i',
28403                 cls : 'fa ' + this.icon
28404             })
28405         }
28406         
28407         var cfg = {
28408             tag : 'li',
28409             cn : [
28410                 {
28411                     tag : 'a',
28412                     href : this.href || '#',
28413                     cn : text
28414                 }
28415             ]
28416         };
28417         
28418         if(this.disable){
28419             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28420         }
28421         
28422         if(this.submenu){
28423             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28424             
28425             if(this.pos == 'left'){
28426                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28427             }
28428         }
28429         
28430         return cfg;
28431     },
28432     
28433     initEvents : function() 
28434     {
28435         this.el.on('mouseover', this.onMouseOver, this);
28436         this.el.on('mouseout', this.onMouseOut, this);
28437         
28438         this.el.select('a', true).first().on('click', this.onClick, this);
28439         
28440     },
28441     
28442     onClick : function(e)
28443     {
28444         if(this.preventDefault){
28445             e.preventDefault();
28446         }
28447         
28448         this.fireEvent("click", this, e);
28449     },
28450     
28451     onMouseOver : function(e)
28452     {
28453         if(this.submenu && this.pos == 'left'){
28454             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28455         }
28456         
28457         this.fireEvent("mouseover", this, e);
28458     },
28459     
28460     onMouseOut : function(e)
28461     {
28462         this.fireEvent("mouseout", this, e);
28463     }
28464 });
28465
28466  
28467
28468  /*
28469  * - LGPL
28470  *
28471  * menu separator
28472  * 
28473  */
28474 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28475
28476 /**
28477  * @class Roo.bootstrap.menu.Separator
28478  * @extends Roo.bootstrap.Component
28479  * Bootstrap Separator class
28480  * 
28481  * @constructor
28482  * Create a new Separator
28483  * @param {Object} config The config object
28484  */
28485
28486
28487 Roo.bootstrap.menu.Separator = function(config){
28488     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28489 };
28490
28491 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28492     
28493     getAutoCreate : function(){
28494         var cfg = {
28495             tag : 'li',
28496             cls: 'divider'
28497         };
28498         
28499         return cfg;
28500     }
28501    
28502 });
28503
28504  
28505
28506  /*
28507  * - LGPL
28508  *
28509  * Tooltip
28510  * 
28511  */
28512
28513 /**
28514  * @class Roo.bootstrap.Tooltip
28515  * Bootstrap Tooltip class
28516  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28517  * to determine which dom element triggers the tooltip.
28518  * 
28519  * It needs to add support for additional attributes like tooltip-position
28520  * 
28521  * @constructor
28522  * Create a new Toolti
28523  * @param {Object} config The config object
28524  */
28525
28526 Roo.bootstrap.Tooltip = function(config){
28527     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28528     
28529     this.alignment = Roo.bootstrap.Tooltip.alignment;
28530     
28531     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28532         this.alignment = config.alignment;
28533     }
28534     
28535 };
28536
28537 Roo.apply(Roo.bootstrap.Tooltip, {
28538     /**
28539      * @function init initialize tooltip monitoring.
28540      * @static
28541      */
28542     currentEl : false,
28543     currentTip : false,
28544     currentRegion : false,
28545     
28546     //  init : delay?
28547     
28548     init : function()
28549     {
28550         Roo.get(document).on('mouseover', this.enter ,this);
28551         Roo.get(document).on('mouseout', this.leave, this);
28552          
28553         
28554         this.currentTip = new Roo.bootstrap.Tooltip();
28555     },
28556     
28557     enter : function(ev)
28558     {
28559         var dom = ev.getTarget();
28560         
28561         //Roo.log(['enter',dom]);
28562         var el = Roo.fly(dom);
28563         if (this.currentEl) {
28564             //Roo.log(dom);
28565             //Roo.log(this.currentEl);
28566             //Roo.log(this.currentEl.contains(dom));
28567             if (this.currentEl == el) {
28568                 return;
28569             }
28570             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28571                 return;
28572             }
28573
28574         }
28575         
28576         if (this.currentTip.el) {
28577             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28578         }    
28579         //Roo.log(ev);
28580         
28581         if(!el || el.dom == document){
28582             return;
28583         }
28584         
28585         var bindEl = el;
28586         
28587         // you can not look for children, as if el is the body.. then everythign is the child..
28588         if (!el.attr('tooltip')) { //
28589             if (!el.select("[tooltip]").elements.length) {
28590                 return;
28591             }
28592             // is the mouse over this child...?
28593             bindEl = el.select("[tooltip]").first();
28594             var xy = ev.getXY();
28595             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28596                 //Roo.log("not in region.");
28597                 return;
28598             }
28599             //Roo.log("child element over..");
28600             
28601         }
28602         this.currentEl = bindEl;
28603         this.currentTip.bind(bindEl);
28604         this.currentRegion = Roo.lib.Region.getRegion(dom);
28605         this.currentTip.enter();
28606         
28607     },
28608     leave : function(ev)
28609     {
28610         var dom = ev.getTarget();
28611         //Roo.log(['leave',dom]);
28612         if (!this.currentEl) {
28613             return;
28614         }
28615         
28616         
28617         if (dom != this.currentEl.dom) {
28618             return;
28619         }
28620         var xy = ev.getXY();
28621         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28622             return;
28623         }
28624         // only activate leave if mouse cursor is outside... bounding box..
28625         
28626         
28627         
28628         
28629         if (this.currentTip) {
28630             this.currentTip.leave();
28631         }
28632         //Roo.log('clear currentEl');
28633         this.currentEl = false;
28634         
28635         
28636     },
28637     alignment : {
28638         'left' : ['r-l', [-2,0], 'right'],
28639         'right' : ['l-r', [2,0], 'left'],
28640         'bottom' : ['t-b', [0,2], 'top'],
28641         'top' : [ 'b-t', [0,-2], 'bottom']
28642     }
28643     
28644 });
28645
28646
28647 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28648     
28649     
28650     bindEl : false,
28651     
28652     delay : null, // can be { show : 300 , hide: 500}
28653     
28654     timeout : null,
28655     
28656     hoverState : null, //???
28657     
28658     placement : 'bottom', 
28659     
28660     alignment : false,
28661     
28662     getAutoCreate : function(){
28663     
28664         var cfg = {
28665            cls : 'tooltip',   
28666            role : 'tooltip',
28667            cn : [
28668                 {
28669                     cls : 'tooltip-arrow arrow'
28670                 },
28671                 {
28672                     cls : 'tooltip-inner'
28673                 }
28674            ]
28675         };
28676         
28677         return cfg;
28678     },
28679     bind : function(el)
28680     {
28681         this.bindEl = el;
28682     },
28683     
28684     initEvents : function()
28685     {
28686         this.arrowEl = this.el.select('.arrow', true).first();
28687         this.innerEl = this.el.select('.tooltip-inner', true).first();
28688     },
28689     
28690     enter : function () {
28691        
28692         if (this.timeout != null) {
28693             clearTimeout(this.timeout);
28694         }
28695         
28696         this.hoverState = 'in';
28697          //Roo.log("enter - show");
28698         if (!this.delay || !this.delay.show) {
28699             this.show();
28700             return;
28701         }
28702         var _t = this;
28703         this.timeout = setTimeout(function () {
28704             if (_t.hoverState == 'in') {
28705                 _t.show();
28706             }
28707         }, this.delay.show);
28708     },
28709     leave : function()
28710     {
28711         clearTimeout(this.timeout);
28712     
28713         this.hoverState = 'out';
28714          if (!this.delay || !this.delay.hide) {
28715             this.hide();
28716             return;
28717         }
28718        
28719         var _t = this;
28720         this.timeout = setTimeout(function () {
28721             //Roo.log("leave - timeout");
28722             
28723             if (_t.hoverState == 'out') {
28724                 _t.hide();
28725                 Roo.bootstrap.Tooltip.currentEl = false;
28726             }
28727         }, delay);
28728     },
28729     
28730     show : function (msg)
28731     {
28732         if (!this.el) {
28733             this.render(document.body);
28734         }
28735         // set content.
28736         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28737         
28738         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28739         
28740         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28741         
28742         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28743                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28744         
28745         var placement = typeof this.placement == 'function' ?
28746             this.placement.call(this, this.el, on_el) :
28747             this.placement;
28748             
28749         var autoToken = /\s?auto?\s?/i;
28750         var autoPlace = autoToken.test(placement);
28751         if (autoPlace) {
28752             placement = placement.replace(autoToken, '') || 'top';
28753         }
28754         
28755         //this.el.detach()
28756         //this.el.setXY([0,0]);
28757         this.el.show();
28758         //this.el.dom.style.display='block';
28759         
28760         //this.el.appendTo(on_el);
28761         
28762         var p = this.getPosition();
28763         var box = this.el.getBox();
28764         
28765         if (autoPlace) {
28766             // fixme..
28767         }
28768         
28769         var align = this.alignment[placement];
28770         
28771         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28772         
28773         if(placement == 'top' || placement == 'bottom'){
28774             if(xy[0] < 0){
28775                 placement = 'right';
28776             }
28777             
28778             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28779                 placement = 'left';
28780             }
28781             
28782             var scroll = Roo.select('body', true).first().getScroll();
28783             
28784             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28785                 placement = 'top';
28786             }
28787             
28788             align = this.alignment[placement];
28789             
28790             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28791             
28792         }
28793         
28794         this.el.alignTo(this.bindEl, align[0],align[1]);
28795         //var arrow = this.el.select('.arrow',true).first();
28796         //arrow.set(align[2], 
28797         
28798         this.el.addClass(placement);
28799         this.el.addClass("bs-tooltip-"+ placement);
28800         
28801         this.el.addClass('in fade show');
28802         
28803         this.hoverState = null;
28804         
28805         if (this.el.hasClass('fade')) {
28806             // fade it?
28807         }
28808         
28809         
28810         
28811         
28812         
28813     },
28814     hide : function()
28815     {
28816          
28817         if (!this.el) {
28818             return;
28819         }
28820         //this.el.setXY([0,0]);
28821         this.el.removeClass(['show', 'in']);
28822         //this.el.hide();
28823         
28824     }
28825     
28826 });
28827  
28828
28829  /*
28830  * - LGPL
28831  *
28832  * Location Picker
28833  * 
28834  */
28835
28836 /**
28837  * @class Roo.bootstrap.LocationPicker
28838  * @extends Roo.bootstrap.Component
28839  * Bootstrap LocationPicker class
28840  * @cfg {Number} latitude Position when init default 0
28841  * @cfg {Number} longitude Position when init default 0
28842  * @cfg {Number} zoom default 15
28843  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28844  * @cfg {Boolean} mapTypeControl default false
28845  * @cfg {Boolean} disableDoubleClickZoom default false
28846  * @cfg {Boolean} scrollwheel default true
28847  * @cfg {Boolean} streetViewControl default false
28848  * @cfg {Number} radius default 0
28849  * @cfg {String} locationName
28850  * @cfg {Boolean} draggable default true
28851  * @cfg {Boolean} enableAutocomplete default false
28852  * @cfg {Boolean} enableReverseGeocode default true
28853  * @cfg {String} markerTitle
28854  * 
28855  * @constructor
28856  * Create a new LocationPicker
28857  * @param {Object} config The config object
28858  */
28859
28860
28861 Roo.bootstrap.LocationPicker = function(config){
28862     
28863     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28864     
28865     this.addEvents({
28866         /**
28867          * @event initial
28868          * Fires when the picker initialized.
28869          * @param {Roo.bootstrap.LocationPicker} this
28870          * @param {Google Location} location
28871          */
28872         initial : true,
28873         /**
28874          * @event positionchanged
28875          * Fires when the picker position changed.
28876          * @param {Roo.bootstrap.LocationPicker} this
28877          * @param {Google Location} location
28878          */
28879         positionchanged : true,
28880         /**
28881          * @event resize
28882          * Fires when the map resize.
28883          * @param {Roo.bootstrap.LocationPicker} this
28884          */
28885         resize : true,
28886         /**
28887          * @event show
28888          * Fires when the map show.
28889          * @param {Roo.bootstrap.LocationPicker} this
28890          */
28891         show : true,
28892         /**
28893          * @event hide
28894          * Fires when the map hide.
28895          * @param {Roo.bootstrap.LocationPicker} this
28896          */
28897         hide : true,
28898         /**
28899          * @event mapClick
28900          * Fires when click the map.
28901          * @param {Roo.bootstrap.LocationPicker} this
28902          * @param {Map event} e
28903          */
28904         mapClick : true,
28905         /**
28906          * @event mapRightClick
28907          * Fires when right click the map.
28908          * @param {Roo.bootstrap.LocationPicker} this
28909          * @param {Map event} e
28910          */
28911         mapRightClick : true,
28912         /**
28913          * @event markerClick
28914          * Fires when click the marker.
28915          * @param {Roo.bootstrap.LocationPicker} this
28916          * @param {Map event} e
28917          */
28918         markerClick : true,
28919         /**
28920          * @event markerRightClick
28921          * Fires when right click the marker.
28922          * @param {Roo.bootstrap.LocationPicker} this
28923          * @param {Map event} e
28924          */
28925         markerRightClick : true,
28926         /**
28927          * @event OverlayViewDraw
28928          * Fires when OverlayView Draw
28929          * @param {Roo.bootstrap.LocationPicker} this
28930          */
28931         OverlayViewDraw : true,
28932         /**
28933          * @event OverlayViewOnAdd
28934          * Fires when OverlayView Draw
28935          * @param {Roo.bootstrap.LocationPicker} this
28936          */
28937         OverlayViewOnAdd : true,
28938         /**
28939          * @event OverlayViewOnRemove
28940          * Fires when OverlayView Draw
28941          * @param {Roo.bootstrap.LocationPicker} this
28942          */
28943         OverlayViewOnRemove : true,
28944         /**
28945          * @event OverlayViewShow
28946          * Fires when OverlayView Draw
28947          * @param {Roo.bootstrap.LocationPicker} this
28948          * @param {Pixel} cpx
28949          */
28950         OverlayViewShow : true,
28951         /**
28952          * @event OverlayViewHide
28953          * Fires when OverlayView Draw
28954          * @param {Roo.bootstrap.LocationPicker} this
28955          */
28956         OverlayViewHide : true,
28957         /**
28958          * @event loadexception
28959          * Fires when load google lib failed.
28960          * @param {Roo.bootstrap.LocationPicker} this
28961          */
28962         loadexception : true
28963     });
28964         
28965 };
28966
28967 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28968     
28969     gMapContext: false,
28970     
28971     latitude: 0,
28972     longitude: 0,
28973     zoom: 15,
28974     mapTypeId: false,
28975     mapTypeControl: false,
28976     disableDoubleClickZoom: false,
28977     scrollwheel: true,
28978     streetViewControl: false,
28979     radius: 0,
28980     locationName: '',
28981     draggable: true,
28982     enableAutocomplete: false,
28983     enableReverseGeocode: true,
28984     markerTitle: '',
28985     
28986     getAutoCreate: function()
28987     {
28988
28989         var cfg = {
28990             tag: 'div',
28991             cls: 'roo-location-picker'
28992         };
28993         
28994         return cfg
28995     },
28996     
28997     initEvents: function(ct, position)
28998     {       
28999         if(!this.el.getWidth() || this.isApplied()){
29000             return;
29001         }
29002         
29003         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29004         
29005         this.initial();
29006     },
29007     
29008     initial: function()
29009     {
29010         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29011             this.fireEvent('loadexception', this);
29012             return;
29013         }
29014         
29015         if(!this.mapTypeId){
29016             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29017         }
29018         
29019         this.gMapContext = this.GMapContext();
29020         
29021         this.initOverlayView();
29022         
29023         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29024         
29025         var _this = this;
29026                 
29027         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29028             _this.setPosition(_this.gMapContext.marker.position);
29029         });
29030         
29031         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29032             _this.fireEvent('mapClick', this, event);
29033             
29034         });
29035
29036         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29037             _this.fireEvent('mapRightClick', this, event);
29038             
29039         });
29040         
29041         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29042             _this.fireEvent('markerClick', this, event);
29043             
29044         });
29045
29046         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29047             _this.fireEvent('markerRightClick', this, event);
29048             
29049         });
29050         
29051         this.setPosition(this.gMapContext.location);
29052         
29053         this.fireEvent('initial', this, this.gMapContext.location);
29054     },
29055     
29056     initOverlayView: function()
29057     {
29058         var _this = this;
29059         
29060         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29061             
29062             draw: function()
29063             {
29064                 _this.fireEvent('OverlayViewDraw', _this);
29065             },
29066             
29067             onAdd: function()
29068             {
29069                 _this.fireEvent('OverlayViewOnAdd', _this);
29070             },
29071             
29072             onRemove: function()
29073             {
29074                 _this.fireEvent('OverlayViewOnRemove', _this);
29075             },
29076             
29077             show: function(cpx)
29078             {
29079                 _this.fireEvent('OverlayViewShow', _this, cpx);
29080             },
29081             
29082             hide: function()
29083             {
29084                 _this.fireEvent('OverlayViewHide', _this);
29085             }
29086             
29087         });
29088     },
29089     
29090     fromLatLngToContainerPixel: function(event)
29091     {
29092         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29093     },
29094     
29095     isApplied: function() 
29096     {
29097         return this.getGmapContext() == false ? false : true;
29098     },
29099     
29100     getGmapContext: function() 
29101     {
29102         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29103     },
29104     
29105     GMapContext: function() 
29106     {
29107         var position = new google.maps.LatLng(this.latitude, this.longitude);
29108         
29109         var _map = new google.maps.Map(this.el.dom, {
29110             center: position,
29111             zoom: this.zoom,
29112             mapTypeId: this.mapTypeId,
29113             mapTypeControl: this.mapTypeControl,
29114             disableDoubleClickZoom: this.disableDoubleClickZoom,
29115             scrollwheel: this.scrollwheel,
29116             streetViewControl: this.streetViewControl,
29117             locationName: this.locationName,
29118             draggable: this.draggable,
29119             enableAutocomplete: this.enableAutocomplete,
29120             enableReverseGeocode: this.enableReverseGeocode
29121         });
29122         
29123         var _marker = new google.maps.Marker({
29124             position: position,
29125             map: _map,
29126             title: this.markerTitle,
29127             draggable: this.draggable
29128         });
29129         
29130         return {
29131             map: _map,
29132             marker: _marker,
29133             circle: null,
29134             location: position,
29135             radius: this.radius,
29136             locationName: this.locationName,
29137             addressComponents: {
29138                 formatted_address: null,
29139                 addressLine1: null,
29140                 addressLine2: null,
29141                 streetName: null,
29142                 streetNumber: null,
29143                 city: null,
29144                 district: null,
29145                 state: null,
29146                 stateOrProvince: null
29147             },
29148             settings: this,
29149             domContainer: this.el.dom,
29150             geodecoder: new google.maps.Geocoder()
29151         };
29152     },
29153     
29154     drawCircle: function(center, radius, options) 
29155     {
29156         if (this.gMapContext.circle != null) {
29157             this.gMapContext.circle.setMap(null);
29158         }
29159         if (radius > 0) {
29160             radius *= 1;
29161             options = Roo.apply({}, options, {
29162                 strokeColor: "#0000FF",
29163                 strokeOpacity: .35,
29164                 strokeWeight: 2,
29165                 fillColor: "#0000FF",
29166                 fillOpacity: .2
29167             });
29168             
29169             options.map = this.gMapContext.map;
29170             options.radius = radius;
29171             options.center = center;
29172             this.gMapContext.circle = new google.maps.Circle(options);
29173             return this.gMapContext.circle;
29174         }
29175         
29176         return null;
29177     },
29178     
29179     setPosition: function(location) 
29180     {
29181         this.gMapContext.location = location;
29182         this.gMapContext.marker.setPosition(location);
29183         this.gMapContext.map.panTo(location);
29184         this.drawCircle(location, this.gMapContext.radius, {});
29185         
29186         var _this = this;
29187         
29188         if (this.gMapContext.settings.enableReverseGeocode) {
29189             this.gMapContext.geodecoder.geocode({
29190                 latLng: this.gMapContext.location
29191             }, function(results, status) {
29192                 
29193                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29194                     _this.gMapContext.locationName = results[0].formatted_address;
29195                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29196                     
29197                     _this.fireEvent('positionchanged', this, location);
29198                 }
29199             });
29200             
29201             return;
29202         }
29203         
29204         this.fireEvent('positionchanged', this, location);
29205     },
29206     
29207     resize: function()
29208     {
29209         google.maps.event.trigger(this.gMapContext.map, "resize");
29210         
29211         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29212         
29213         this.fireEvent('resize', this);
29214     },
29215     
29216     setPositionByLatLng: function(latitude, longitude)
29217     {
29218         this.setPosition(new google.maps.LatLng(latitude, longitude));
29219     },
29220     
29221     getCurrentPosition: function() 
29222     {
29223         return {
29224             latitude: this.gMapContext.location.lat(),
29225             longitude: this.gMapContext.location.lng()
29226         };
29227     },
29228     
29229     getAddressName: function() 
29230     {
29231         return this.gMapContext.locationName;
29232     },
29233     
29234     getAddressComponents: function() 
29235     {
29236         return this.gMapContext.addressComponents;
29237     },
29238     
29239     address_component_from_google_geocode: function(address_components) 
29240     {
29241         var result = {};
29242         
29243         for (var i = 0; i < address_components.length; i++) {
29244             var component = address_components[i];
29245             if (component.types.indexOf("postal_code") >= 0) {
29246                 result.postalCode = component.short_name;
29247             } else if (component.types.indexOf("street_number") >= 0) {
29248                 result.streetNumber = component.short_name;
29249             } else if (component.types.indexOf("route") >= 0) {
29250                 result.streetName = component.short_name;
29251             } else if (component.types.indexOf("neighborhood") >= 0) {
29252                 result.city = component.short_name;
29253             } else if (component.types.indexOf("locality") >= 0) {
29254                 result.city = component.short_name;
29255             } else if (component.types.indexOf("sublocality") >= 0) {
29256                 result.district = component.short_name;
29257             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29258                 result.stateOrProvince = component.short_name;
29259             } else if (component.types.indexOf("country") >= 0) {
29260                 result.country = component.short_name;
29261             }
29262         }
29263         
29264         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29265         result.addressLine2 = "";
29266         return result;
29267     },
29268     
29269     setZoomLevel: function(zoom)
29270     {
29271         this.gMapContext.map.setZoom(zoom);
29272     },
29273     
29274     show: function()
29275     {
29276         if(!this.el){
29277             return;
29278         }
29279         
29280         this.el.show();
29281         
29282         this.resize();
29283         
29284         this.fireEvent('show', this);
29285     },
29286     
29287     hide: function()
29288     {
29289         if(!this.el){
29290             return;
29291         }
29292         
29293         this.el.hide();
29294         
29295         this.fireEvent('hide', this);
29296     }
29297     
29298 });
29299
29300 Roo.apply(Roo.bootstrap.LocationPicker, {
29301     
29302     OverlayView : function(map, options)
29303     {
29304         options = options || {};
29305         
29306         this.setMap(map);
29307     }
29308     
29309     
29310 });/**
29311  * @class Roo.bootstrap.Alert
29312  * @extends Roo.bootstrap.Component
29313  * Bootstrap Alert class - shows an alert area box
29314  * eg
29315  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29316   Enter a valid email address
29317 </div>
29318  * @licence LGPL
29319  * @cfg {String} title The title of alert
29320  * @cfg {String} html The content of alert
29321  * @cfg {String} weight (  success | info | warning | danger )
29322  * @cfg {String} faicon font-awesomeicon
29323  * 
29324  * @constructor
29325  * Create a new alert
29326  * @param {Object} config The config object
29327  */
29328
29329
29330 Roo.bootstrap.Alert = function(config){
29331     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29332     
29333 };
29334
29335 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29336     
29337     title: '',
29338     html: '',
29339     weight: false,
29340     faicon: false,
29341     
29342     getAutoCreate : function()
29343     {
29344         
29345         var cfg = {
29346             tag : 'div',
29347             cls : 'alert',
29348             cn : [
29349                 {
29350                     tag : 'i',
29351                     cls : 'roo-alert-icon'
29352                     
29353                 },
29354                 {
29355                     tag : 'b',
29356                     cls : 'roo-alert-title',
29357                     html : this.title
29358                 },
29359                 {
29360                     tag : 'span',
29361                     cls : 'roo-alert-text',
29362                     html : this.html
29363                 }
29364             ]
29365         };
29366         
29367         if(this.faicon){
29368             cfg.cn[0].cls += ' fa ' + this.faicon;
29369         }
29370         
29371         if(this.weight){
29372             cfg.cls += ' alert-' + this.weight;
29373         }
29374         
29375         return cfg;
29376     },
29377     
29378     initEvents: function() 
29379     {
29380         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29381     },
29382     
29383     setTitle : function(str)
29384     {
29385         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29386     },
29387     
29388     setText : function(str)
29389     {
29390         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29391     },
29392     
29393     setWeight : function(weight)
29394     {
29395         if(this.weight){
29396             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29397         }
29398         
29399         this.weight = weight;
29400         
29401         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29402     },
29403     
29404     setIcon : function(icon)
29405     {
29406         if(this.faicon){
29407             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29408         }
29409         
29410         this.faicon = icon;
29411         
29412         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29413     },
29414     
29415     hide: function() 
29416     {
29417         this.el.hide();   
29418     },
29419     
29420     show: function() 
29421     {  
29422         this.el.show();   
29423     }
29424     
29425 });
29426
29427  
29428 /*
29429 * Licence: LGPL
29430 */
29431
29432 /**
29433  * @class Roo.bootstrap.UploadCropbox
29434  * @extends Roo.bootstrap.Component
29435  * Bootstrap UploadCropbox class
29436  * @cfg {String} emptyText show when image has been loaded
29437  * @cfg {String} rotateNotify show when image too small to rotate
29438  * @cfg {Number} errorTimeout default 3000
29439  * @cfg {Number} minWidth default 300
29440  * @cfg {Number} minHeight default 300
29441  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29442  * @cfg {Boolean} isDocument (true|false) default false
29443  * @cfg {String} url action url
29444  * @cfg {String} paramName default 'imageUpload'
29445  * @cfg {String} method default POST
29446  * @cfg {Boolean} loadMask (true|false) default true
29447  * @cfg {Boolean} loadingText default 'Loading...'
29448  * 
29449  * @constructor
29450  * Create a new UploadCropbox
29451  * @param {Object} config The config object
29452  */
29453
29454 Roo.bootstrap.UploadCropbox = function(config){
29455     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29456     
29457     this.addEvents({
29458         /**
29459          * @event beforeselectfile
29460          * Fire before select file
29461          * @param {Roo.bootstrap.UploadCropbox} this
29462          */
29463         "beforeselectfile" : true,
29464         /**
29465          * @event initial
29466          * Fire after initEvent
29467          * @param {Roo.bootstrap.UploadCropbox} this
29468          */
29469         "initial" : true,
29470         /**
29471          * @event crop
29472          * Fire after initEvent
29473          * @param {Roo.bootstrap.UploadCropbox} this
29474          * @param {String} data
29475          */
29476         "crop" : true,
29477         /**
29478          * @event prepare
29479          * Fire when preparing the file data
29480          * @param {Roo.bootstrap.UploadCropbox} this
29481          * @param {Object} file
29482          */
29483         "prepare" : true,
29484         /**
29485          * @event exception
29486          * Fire when get exception
29487          * @param {Roo.bootstrap.UploadCropbox} this
29488          * @param {XMLHttpRequest} xhr
29489          */
29490         "exception" : true,
29491         /**
29492          * @event beforeloadcanvas
29493          * Fire before load the canvas
29494          * @param {Roo.bootstrap.UploadCropbox} this
29495          * @param {String} src
29496          */
29497         "beforeloadcanvas" : true,
29498         /**
29499          * @event trash
29500          * Fire when trash image
29501          * @param {Roo.bootstrap.UploadCropbox} this
29502          */
29503         "trash" : true,
29504         /**
29505          * @event download
29506          * Fire when download the image
29507          * @param {Roo.bootstrap.UploadCropbox} this
29508          */
29509         "download" : true,
29510         /**
29511          * @event footerbuttonclick
29512          * Fire when footerbuttonclick
29513          * @param {Roo.bootstrap.UploadCropbox} this
29514          * @param {String} type
29515          */
29516         "footerbuttonclick" : true,
29517         /**
29518          * @event resize
29519          * Fire when resize
29520          * @param {Roo.bootstrap.UploadCropbox} this
29521          */
29522         "resize" : true,
29523         /**
29524          * @event rotate
29525          * Fire when rotate the image
29526          * @param {Roo.bootstrap.UploadCropbox} this
29527          * @param {String} pos
29528          */
29529         "rotate" : true,
29530         /**
29531          * @event inspect
29532          * Fire when inspect the file
29533          * @param {Roo.bootstrap.UploadCropbox} this
29534          * @param {Object} file
29535          */
29536         "inspect" : true,
29537         /**
29538          * @event upload
29539          * Fire when xhr upload the file
29540          * @param {Roo.bootstrap.UploadCropbox} this
29541          * @param {Object} data
29542          */
29543         "upload" : true,
29544         /**
29545          * @event arrange
29546          * Fire when arrange the file data
29547          * @param {Roo.bootstrap.UploadCropbox} this
29548          * @param {Object} formData
29549          */
29550         "arrange" : true
29551     });
29552     
29553     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29554 };
29555
29556 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29557     
29558     emptyText : 'Click to upload image',
29559     rotateNotify : 'Image is too small to rotate',
29560     errorTimeout : 3000,
29561     scale : 0,
29562     baseScale : 1,
29563     rotate : 0,
29564     dragable : false,
29565     pinching : false,
29566     mouseX : 0,
29567     mouseY : 0,
29568     cropData : false,
29569     minWidth : 300,
29570     minHeight : 300,
29571     file : false,
29572     exif : {},
29573     baseRotate : 1,
29574     cropType : 'image/jpeg',
29575     buttons : false,
29576     canvasLoaded : false,
29577     isDocument : false,
29578     method : 'POST',
29579     paramName : 'imageUpload',
29580     loadMask : true,
29581     loadingText : 'Loading...',
29582     maskEl : false,
29583     
29584     getAutoCreate : function()
29585     {
29586         var cfg = {
29587             tag : 'div',
29588             cls : 'roo-upload-cropbox',
29589             cn : [
29590                 {
29591                     tag : 'input',
29592                     cls : 'roo-upload-cropbox-selector',
29593                     type : 'file'
29594                 },
29595                 {
29596                     tag : 'div',
29597                     cls : 'roo-upload-cropbox-body',
29598                     style : 'cursor:pointer',
29599                     cn : [
29600                         {
29601                             tag : 'div',
29602                             cls : 'roo-upload-cropbox-preview'
29603                         },
29604                         {
29605                             tag : 'div',
29606                             cls : 'roo-upload-cropbox-thumb'
29607                         },
29608                         {
29609                             tag : 'div',
29610                             cls : 'roo-upload-cropbox-empty-notify',
29611                             html : this.emptyText
29612                         },
29613                         {
29614                             tag : 'div',
29615                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29616                             html : this.rotateNotify
29617                         }
29618                     ]
29619                 },
29620                 {
29621                     tag : 'div',
29622                     cls : 'roo-upload-cropbox-footer',
29623                     cn : {
29624                         tag : 'div',
29625                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29626                         cn : []
29627                     }
29628                 }
29629             ]
29630         };
29631         
29632         return cfg;
29633     },
29634     
29635     onRender : function(ct, position)
29636     {
29637         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29638         
29639         if (this.buttons.length) {
29640             
29641             Roo.each(this.buttons, function(bb) {
29642                 
29643                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29644                 
29645                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29646                 
29647             }, this);
29648         }
29649         
29650         if(this.loadMask){
29651             this.maskEl = this.el;
29652         }
29653     },
29654     
29655     initEvents : function()
29656     {
29657         this.urlAPI = (window.createObjectURL && window) || 
29658                                 (window.URL && URL.revokeObjectURL && URL) || 
29659                                 (window.webkitURL && webkitURL);
29660                         
29661         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29662         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29663         
29664         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29665         this.selectorEl.hide();
29666         
29667         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29668         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29669         
29670         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29671         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29672         this.thumbEl.hide();
29673         
29674         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29675         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29676         
29677         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29678         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29679         this.errorEl.hide();
29680         
29681         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29682         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29683         this.footerEl.hide();
29684         
29685         this.setThumbBoxSize();
29686         
29687         this.bind();
29688         
29689         this.resize();
29690         
29691         this.fireEvent('initial', this);
29692     },
29693
29694     bind : function()
29695     {
29696         var _this = this;
29697         
29698         window.addEventListener("resize", function() { _this.resize(); } );
29699         
29700         this.bodyEl.on('click', this.beforeSelectFile, this);
29701         
29702         if(Roo.isTouch){
29703             this.bodyEl.on('touchstart', this.onTouchStart, this);
29704             this.bodyEl.on('touchmove', this.onTouchMove, this);
29705             this.bodyEl.on('touchend', this.onTouchEnd, this);
29706         }
29707         
29708         if(!Roo.isTouch){
29709             this.bodyEl.on('mousedown', this.onMouseDown, this);
29710             this.bodyEl.on('mousemove', this.onMouseMove, this);
29711             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29712             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29713             Roo.get(document).on('mouseup', this.onMouseUp, this);
29714         }
29715         
29716         this.selectorEl.on('change', this.onFileSelected, this);
29717     },
29718     
29719     reset : function()
29720     {    
29721         this.scale = 0;
29722         this.baseScale = 1;
29723         this.rotate = 0;
29724         this.baseRotate = 1;
29725         this.dragable = false;
29726         this.pinching = false;
29727         this.mouseX = 0;
29728         this.mouseY = 0;
29729         this.cropData = false;
29730         this.notifyEl.dom.innerHTML = this.emptyText;
29731         
29732         this.selectorEl.dom.value = '';
29733         
29734     },
29735     
29736     resize : function()
29737     {
29738         if(this.fireEvent('resize', this) != false){
29739             this.setThumbBoxPosition();
29740             this.setCanvasPosition();
29741         }
29742     },
29743     
29744     onFooterButtonClick : function(e, el, o, type)
29745     {
29746         switch (type) {
29747             case 'rotate-left' :
29748                 this.onRotateLeft(e);
29749                 break;
29750             case 'rotate-right' :
29751                 this.onRotateRight(e);
29752                 break;
29753             case 'picture' :
29754                 this.beforeSelectFile(e);
29755                 break;
29756             case 'trash' :
29757                 this.trash(e);
29758                 break;
29759             case 'crop' :
29760                 this.crop(e);
29761                 break;
29762             case 'download' :
29763                 this.download(e);
29764                 break;
29765             default :
29766                 break;
29767         }
29768         
29769         this.fireEvent('footerbuttonclick', this, type);
29770     },
29771     
29772     beforeSelectFile : function(e)
29773     {
29774         e.preventDefault();
29775         
29776         if(this.fireEvent('beforeselectfile', this) != false){
29777             this.selectorEl.dom.click();
29778         }
29779     },
29780     
29781     onFileSelected : function(e)
29782     {
29783         e.preventDefault();
29784         
29785         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29786             return;
29787         }
29788         
29789         var file = this.selectorEl.dom.files[0];
29790         
29791         if(this.fireEvent('inspect', this, file) != false){
29792             this.prepare(file);
29793         }
29794         
29795     },
29796     
29797     trash : function(e)
29798     {
29799         this.fireEvent('trash', this);
29800     },
29801     
29802     download : function(e)
29803     {
29804         this.fireEvent('download', this);
29805     },
29806     
29807     loadCanvas : function(src)
29808     {   
29809         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29810             
29811             this.reset();
29812             
29813             this.imageEl = document.createElement('img');
29814             
29815             var _this = this;
29816             
29817             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29818             
29819             this.imageEl.src = src;
29820         }
29821     },
29822     
29823     onLoadCanvas : function()
29824     {   
29825         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29826         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29827         
29828         this.bodyEl.un('click', this.beforeSelectFile, this);
29829         
29830         this.notifyEl.hide();
29831         this.thumbEl.show();
29832         this.footerEl.show();
29833         
29834         this.baseRotateLevel();
29835         
29836         if(this.isDocument){
29837             this.setThumbBoxSize();
29838         }
29839         
29840         this.setThumbBoxPosition();
29841         
29842         this.baseScaleLevel();
29843         
29844         this.draw();
29845         
29846         this.resize();
29847         
29848         this.canvasLoaded = true;
29849         
29850         if(this.loadMask){
29851             this.maskEl.unmask();
29852         }
29853         
29854     },
29855     
29856     setCanvasPosition : function()
29857     {   
29858         if(!this.canvasEl){
29859             return;
29860         }
29861         
29862         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29863         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29864         
29865         this.previewEl.setLeft(pw);
29866         this.previewEl.setTop(ph);
29867         
29868     },
29869     
29870     onMouseDown : function(e)
29871     {   
29872         e.stopEvent();
29873         
29874         this.dragable = true;
29875         this.pinching = false;
29876         
29877         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29878             this.dragable = false;
29879             return;
29880         }
29881         
29882         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29883         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29884         
29885     },
29886     
29887     onMouseMove : function(e)
29888     {   
29889         e.stopEvent();
29890         
29891         if(!this.canvasLoaded){
29892             return;
29893         }
29894         
29895         if (!this.dragable){
29896             return;
29897         }
29898         
29899         var minX = Math.ceil(this.thumbEl.getLeft(true));
29900         var minY = Math.ceil(this.thumbEl.getTop(true));
29901         
29902         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29903         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29904         
29905         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29906         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29907         
29908         x = x - this.mouseX;
29909         y = y - this.mouseY;
29910         
29911         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29912         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29913         
29914         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29915         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29916         
29917         this.previewEl.setLeft(bgX);
29918         this.previewEl.setTop(bgY);
29919         
29920         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29921         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29922     },
29923     
29924     onMouseUp : function(e)
29925     {   
29926         e.stopEvent();
29927         
29928         this.dragable = false;
29929     },
29930     
29931     onMouseWheel : function(e)
29932     {   
29933         e.stopEvent();
29934         
29935         this.startScale = this.scale;
29936         
29937         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29938         
29939         if(!this.zoomable()){
29940             this.scale = this.startScale;
29941             return;
29942         }
29943         
29944         this.draw();
29945         
29946         return;
29947     },
29948     
29949     zoomable : function()
29950     {
29951         var minScale = this.thumbEl.getWidth() / this.minWidth;
29952         
29953         if(this.minWidth < this.minHeight){
29954             minScale = this.thumbEl.getHeight() / this.minHeight;
29955         }
29956         
29957         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29958         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29959         
29960         if(
29961                 this.isDocument &&
29962                 (this.rotate == 0 || this.rotate == 180) && 
29963                 (
29964                     width > this.imageEl.OriginWidth || 
29965                     height > this.imageEl.OriginHeight ||
29966                     (width < this.minWidth && height < this.minHeight)
29967                 )
29968         ){
29969             return false;
29970         }
29971         
29972         if(
29973                 this.isDocument &&
29974                 (this.rotate == 90 || this.rotate == 270) && 
29975                 (
29976                     width > this.imageEl.OriginWidth || 
29977                     height > this.imageEl.OriginHeight ||
29978                     (width < this.minHeight && height < this.minWidth)
29979                 )
29980         ){
29981             return false;
29982         }
29983         
29984         if(
29985                 !this.isDocument &&
29986                 (this.rotate == 0 || this.rotate == 180) && 
29987                 (
29988                     width < this.minWidth || 
29989                     width > this.imageEl.OriginWidth || 
29990                     height < this.minHeight || 
29991                     height > this.imageEl.OriginHeight
29992                 )
29993         ){
29994             return false;
29995         }
29996         
29997         if(
29998                 !this.isDocument &&
29999                 (this.rotate == 90 || this.rotate == 270) && 
30000                 (
30001                     width < this.minHeight || 
30002                     width > this.imageEl.OriginWidth || 
30003                     height < this.minWidth || 
30004                     height > this.imageEl.OriginHeight
30005                 )
30006         ){
30007             return false;
30008         }
30009         
30010         return true;
30011         
30012     },
30013     
30014     onRotateLeft : function(e)
30015     {   
30016         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30017             
30018             var minScale = this.thumbEl.getWidth() / this.minWidth;
30019             
30020             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30021             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30022             
30023             this.startScale = this.scale;
30024             
30025             while (this.getScaleLevel() < minScale){
30026             
30027                 this.scale = this.scale + 1;
30028                 
30029                 if(!this.zoomable()){
30030                     break;
30031                 }
30032                 
30033                 if(
30034                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30035                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30036                 ){
30037                     continue;
30038                 }
30039                 
30040                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30041
30042                 this.draw();
30043                 
30044                 return;
30045             }
30046             
30047             this.scale = this.startScale;
30048             
30049             this.onRotateFail();
30050             
30051             return false;
30052         }
30053         
30054         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30055
30056         if(this.isDocument){
30057             this.setThumbBoxSize();
30058             this.setThumbBoxPosition();
30059             this.setCanvasPosition();
30060         }
30061         
30062         this.draw();
30063         
30064         this.fireEvent('rotate', this, 'left');
30065         
30066     },
30067     
30068     onRotateRight : function(e)
30069     {
30070         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30071             
30072             var minScale = this.thumbEl.getWidth() / this.minWidth;
30073         
30074             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30075             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30076             
30077             this.startScale = this.scale;
30078             
30079             while (this.getScaleLevel() < minScale){
30080             
30081                 this.scale = this.scale + 1;
30082                 
30083                 if(!this.zoomable()){
30084                     break;
30085                 }
30086                 
30087                 if(
30088                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30089                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30090                 ){
30091                     continue;
30092                 }
30093                 
30094                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30095
30096                 this.draw();
30097                 
30098                 return;
30099             }
30100             
30101             this.scale = this.startScale;
30102             
30103             this.onRotateFail();
30104             
30105             return false;
30106         }
30107         
30108         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30109
30110         if(this.isDocument){
30111             this.setThumbBoxSize();
30112             this.setThumbBoxPosition();
30113             this.setCanvasPosition();
30114         }
30115         
30116         this.draw();
30117         
30118         this.fireEvent('rotate', this, 'right');
30119     },
30120     
30121     onRotateFail : function()
30122     {
30123         this.errorEl.show(true);
30124         
30125         var _this = this;
30126         
30127         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30128     },
30129     
30130     draw : function()
30131     {
30132         this.previewEl.dom.innerHTML = '';
30133         
30134         var canvasEl = document.createElement("canvas");
30135         
30136         var contextEl = canvasEl.getContext("2d");
30137         
30138         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30139         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30140         var center = this.imageEl.OriginWidth / 2;
30141         
30142         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30143             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30144             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30145             center = this.imageEl.OriginHeight / 2;
30146         }
30147         
30148         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30149         
30150         contextEl.translate(center, center);
30151         contextEl.rotate(this.rotate * Math.PI / 180);
30152
30153         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30154         
30155         this.canvasEl = document.createElement("canvas");
30156         
30157         this.contextEl = this.canvasEl.getContext("2d");
30158         
30159         switch (this.rotate) {
30160             case 0 :
30161                 
30162                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30163                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30164                 
30165                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30166                 
30167                 break;
30168             case 90 : 
30169                 
30170                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30171                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30172                 
30173                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30174                     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);
30175                     break;
30176                 }
30177                 
30178                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30179                 
30180                 break;
30181             case 180 :
30182                 
30183                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30184                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30185                 
30186                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30187                     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);
30188                     break;
30189                 }
30190                 
30191                 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);
30192                 
30193                 break;
30194             case 270 :
30195                 
30196                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30197                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30198         
30199                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30200                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30201                     break;
30202                 }
30203                 
30204                 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);
30205                 
30206                 break;
30207             default : 
30208                 break;
30209         }
30210         
30211         this.previewEl.appendChild(this.canvasEl);
30212         
30213         this.setCanvasPosition();
30214     },
30215     
30216     crop : function()
30217     {
30218         if(!this.canvasLoaded){
30219             return;
30220         }
30221         
30222         var imageCanvas = document.createElement("canvas");
30223         
30224         var imageContext = imageCanvas.getContext("2d");
30225         
30226         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30227         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30228         
30229         var center = imageCanvas.width / 2;
30230         
30231         imageContext.translate(center, center);
30232         
30233         imageContext.rotate(this.rotate * Math.PI / 180);
30234         
30235         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30236         
30237         var canvas = document.createElement("canvas");
30238         
30239         var context = canvas.getContext("2d");
30240                 
30241         canvas.width = this.minWidth;
30242         canvas.height = this.minHeight;
30243
30244         switch (this.rotate) {
30245             case 0 :
30246                 
30247                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30248                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30249                 
30250                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30251                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30252                 
30253                 var targetWidth = this.minWidth - 2 * x;
30254                 var targetHeight = this.minHeight - 2 * y;
30255                 
30256                 var scale = 1;
30257                 
30258                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30259                     scale = targetWidth / width;
30260                 }
30261                 
30262                 if(x > 0 && y == 0){
30263                     scale = targetHeight / height;
30264                 }
30265                 
30266                 if(x > 0 && y > 0){
30267                     scale = targetWidth / width;
30268                     
30269                     if(width < height){
30270                         scale = targetHeight / height;
30271                     }
30272                 }
30273                 
30274                 context.scale(scale, scale);
30275                 
30276                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30277                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30278
30279                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30280                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30281
30282                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30283                 
30284                 break;
30285             case 90 : 
30286                 
30287                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30288                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30289                 
30290                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30291                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30292                 
30293                 var targetWidth = this.minWidth - 2 * x;
30294                 var targetHeight = this.minHeight - 2 * y;
30295                 
30296                 var scale = 1;
30297                 
30298                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30299                     scale = targetWidth / width;
30300                 }
30301                 
30302                 if(x > 0 && y == 0){
30303                     scale = targetHeight / height;
30304                 }
30305                 
30306                 if(x > 0 && y > 0){
30307                     scale = targetWidth / width;
30308                     
30309                     if(width < height){
30310                         scale = targetHeight / height;
30311                     }
30312                 }
30313                 
30314                 context.scale(scale, scale);
30315                 
30316                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30317                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30318
30319                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30320                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30321                 
30322                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30323                 
30324                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30325                 
30326                 break;
30327             case 180 :
30328                 
30329                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30330                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30331                 
30332                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30333                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30334                 
30335                 var targetWidth = this.minWidth - 2 * x;
30336                 var targetHeight = this.minHeight - 2 * y;
30337                 
30338                 var scale = 1;
30339                 
30340                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30341                     scale = targetWidth / width;
30342                 }
30343                 
30344                 if(x > 0 && y == 0){
30345                     scale = targetHeight / height;
30346                 }
30347                 
30348                 if(x > 0 && y > 0){
30349                     scale = targetWidth / width;
30350                     
30351                     if(width < height){
30352                         scale = targetHeight / height;
30353                     }
30354                 }
30355                 
30356                 context.scale(scale, scale);
30357                 
30358                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30359                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30360
30361                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30362                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30363
30364                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30365                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30366                 
30367                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30368                 
30369                 break;
30370             case 270 :
30371                 
30372                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30373                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30374                 
30375                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30376                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30377                 
30378                 var targetWidth = this.minWidth - 2 * x;
30379                 var targetHeight = this.minHeight - 2 * y;
30380                 
30381                 var scale = 1;
30382                 
30383                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30384                     scale = targetWidth / width;
30385                 }
30386                 
30387                 if(x > 0 && y == 0){
30388                     scale = targetHeight / height;
30389                 }
30390                 
30391                 if(x > 0 && y > 0){
30392                     scale = targetWidth / width;
30393                     
30394                     if(width < height){
30395                         scale = targetHeight / height;
30396                     }
30397                 }
30398                 
30399                 context.scale(scale, scale);
30400                 
30401                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30402                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30403
30404                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30405                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30406                 
30407                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30408                 
30409                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30410                 
30411                 break;
30412             default : 
30413                 break;
30414         }
30415         
30416         this.cropData = canvas.toDataURL(this.cropType);
30417         
30418         if(this.fireEvent('crop', this, this.cropData) !== false){
30419             this.process(this.file, this.cropData);
30420         }
30421         
30422         return;
30423         
30424     },
30425     
30426     setThumbBoxSize : function()
30427     {
30428         var width, height;
30429         
30430         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30431             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30432             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30433             
30434             this.minWidth = width;
30435             this.minHeight = height;
30436             
30437             if(this.rotate == 90 || this.rotate == 270){
30438                 this.minWidth = height;
30439                 this.minHeight = width;
30440             }
30441         }
30442         
30443         height = 300;
30444         width = Math.ceil(this.minWidth * height / this.minHeight);
30445         
30446         if(this.minWidth > this.minHeight){
30447             width = 300;
30448             height = Math.ceil(this.minHeight * width / this.minWidth);
30449         }
30450         
30451         this.thumbEl.setStyle({
30452             width : width + 'px',
30453             height : height + 'px'
30454         });
30455
30456         return;
30457             
30458     },
30459     
30460     setThumbBoxPosition : function()
30461     {
30462         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30463         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30464         
30465         this.thumbEl.setLeft(x);
30466         this.thumbEl.setTop(y);
30467         
30468     },
30469     
30470     baseRotateLevel : function()
30471     {
30472         this.baseRotate = 1;
30473         
30474         if(
30475                 typeof(this.exif) != 'undefined' &&
30476                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30477                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30478         ){
30479             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30480         }
30481         
30482         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30483         
30484     },
30485     
30486     baseScaleLevel : function()
30487     {
30488         var width, height;
30489         
30490         if(this.isDocument){
30491             
30492             if(this.baseRotate == 6 || this.baseRotate == 8){
30493             
30494                 height = this.thumbEl.getHeight();
30495                 this.baseScale = height / this.imageEl.OriginWidth;
30496
30497                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30498                     width = this.thumbEl.getWidth();
30499                     this.baseScale = width / this.imageEl.OriginHeight;
30500                 }
30501
30502                 return;
30503             }
30504
30505             height = this.thumbEl.getHeight();
30506             this.baseScale = height / this.imageEl.OriginHeight;
30507
30508             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30509                 width = this.thumbEl.getWidth();
30510                 this.baseScale = width / this.imageEl.OriginWidth;
30511             }
30512
30513             return;
30514         }
30515         
30516         if(this.baseRotate == 6 || this.baseRotate == 8){
30517             
30518             width = this.thumbEl.getHeight();
30519             this.baseScale = width / this.imageEl.OriginHeight;
30520             
30521             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30522                 height = this.thumbEl.getWidth();
30523                 this.baseScale = height / this.imageEl.OriginHeight;
30524             }
30525             
30526             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30527                 height = this.thumbEl.getWidth();
30528                 this.baseScale = height / this.imageEl.OriginHeight;
30529                 
30530                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30531                     width = this.thumbEl.getHeight();
30532                     this.baseScale = width / this.imageEl.OriginWidth;
30533                 }
30534             }
30535             
30536             return;
30537         }
30538         
30539         width = this.thumbEl.getWidth();
30540         this.baseScale = width / this.imageEl.OriginWidth;
30541         
30542         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30543             height = this.thumbEl.getHeight();
30544             this.baseScale = height / this.imageEl.OriginHeight;
30545         }
30546         
30547         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30548             
30549             height = this.thumbEl.getHeight();
30550             this.baseScale = height / this.imageEl.OriginHeight;
30551             
30552             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30553                 width = this.thumbEl.getWidth();
30554                 this.baseScale = width / this.imageEl.OriginWidth;
30555             }
30556             
30557         }
30558         
30559         return;
30560     },
30561     
30562     getScaleLevel : function()
30563     {
30564         return this.baseScale * Math.pow(1.1, this.scale);
30565     },
30566     
30567     onTouchStart : function(e)
30568     {
30569         if(!this.canvasLoaded){
30570             this.beforeSelectFile(e);
30571             return;
30572         }
30573         
30574         var touches = e.browserEvent.touches;
30575         
30576         if(!touches){
30577             return;
30578         }
30579         
30580         if(touches.length == 1){
30581             this.onMouseDown(e);
30582             return;
30583         }
30584         
30585         if(touches.length != 2){
30586             return;
30587         }
30588         
30589         var coords = [];
30590         
30591         for(var i = 0, finger; finger = touches[i]; i++){
30592             coords.push(finger.pageX, finger.pageY);
30593         }
30594         
30595         var x = Math.pow(coords[0] - coords[2], 2);
30596         var y = Math.pow(coords[1] - coords[3], 2);
30597         
30598         this.startDistance = Math.sqrt(x + y);
30599         
30600         this.startScale = this.scale;
30601         
30602         this.pinching = true;
30603         this.dragable = false;
30604         
30605     },
30606     
30607     onTouchMove : function(e)
30608     {
30609         if(!this.pinching && !this.dragable){
30610             return;
30611         }
30612         
30613         var touches = e.browserEvent.touches;
30614         
30615         if(!touches){
30616             return;
30617         }
30618         
30619         if(this.dragable){
30620             this.onMouseMove(e);
30621             return;
30622         }
30623         
30624         var coords = [];
30625         
30626         for(var i = 0, finger; finger = touches[i]; i++){
30627             coords.push(finger.pageX, finger.pageY);
30628         }
30629         
30630         var x = Math.pow(coords[0] - coords[2], 2);
30631         var y = Math.pow(coords[1] - coords[3], 2);
30632         
30633         this.endDistance = Math.sqrt(x + y);
30634         
30635         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30636         
30637         if(!this.zoomable()){
30638             this.scale = this.startScale;
30639             return;
30640         }
30641         
30642         this.draw();
30643         
30644     },
30645     
30646     onTouchEnd : function(e)
30647     {
30648         this.pinching = false;
30649         this.dragable = false;
30650         
30651     },
30652     
30653     process : function(file, crop)
30654     {
30655         if(this.loadMask){
30656             this.maskEl.mask(this.loadingText);
30657         }
30658         
30659         this.xhr = new XMLHttpRequest();
30660         
30661         file.xhr = this.xhr;
30662
30663         this.xhr.open(this.method, this.url, true);
30664         
30665         var headers = {
30666             "Accept": "application/json",
30667             "Cache-Control": "no-cache",
30668             "X-Requested-With": "XMLHttpRequest"
30669         };
30670         
30671         for (var headerName in headers) {
30672             var headerValue = headers[headerName];
30673             if (headerValue) {
30674                 this.xhr.setRequestHeader(headerName, headerValue);
30675             }
30676         }
30677         
30678         var _this = this;
30679         
30680         this.xhr.onload = function()
30681         {
30682             _this.xhrOnLoad(_this.xhr);
30683         }
30684         
30685         this.xhr.onerror = function()
30686         {
30687             _this.xhrOnError(_this.xhr);
30688         }
30689         
30690         var formData = new FormData();
30691
30692         formData.append('returnHTML', 'NO');
30693         
30694         if(crop){
30695             formData.append('crop', crop);
30696         }
30697         
30698         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30699             formData.append(this.paramName, file, file.name);
30700         }
30701         
30702         if(typeof(file.filename) != 'undefined'){
30703             formData.append('filename', file.filename);
30704         }
30705         
30706         if(typeof(file.mimetype) != 'undefined'){
30707             formData.append('mimetype', file.mimetype);
30708         }
30709         
30710         if(this.fireEvent('arrange', this, formData) != false){
30711             this.xhr.send(formData);
30712         };
30713     },
30714     
30715     xhrOnLoad : function(xhr)
30716     {
30717         if(this.loadMask){
30718             this.maskEl.unmask();
30719         }
30720         
30721         if (xhr.readyState !== 4) {
30722             this.fireEvent('exception', this, xhr);
30723             return;
30724         }
30725
30726         var response = Roo.decode(xhr.responseText);
30727         
30728         if(!response.success){
30729             this.fireEvent('exception', this, xhr);
30730             return;
30731         }
30732         
30733         var response = Roo.decode(xhr.responseText);
30734         
30735         this.fireEvent('upload', this, response);
30736         
30737     },
30738     
30739     xhrOnError : function()
30740     {
30741         if(this.loadMask){
30742             this.maskEl.unmask();
30743         }
30744         
30745         Roo.log('xhr on error');
30746         
30747         var response = Roo.decode(xhr.responseText);
30748           
30749         Roo.log(response);
30750         
30751     },
30752     
30753     prepare : function(file)
30754     {   
30755         if(this.loadMask){
30756             this.maskEl.mask(this.loadingText);
30757         }
30758         
30759         this.file = false;
30760         this.exif = {};
30761         
30762         if(typeof(file) === 'string'){
30763             this.loadCanvas(file);
30764             return;
30765         }
30766         
30767         if(!file || !this.urlAPI){
30768             return;
30769         }
30770         
30771         this.file = file;
30772         this.cropType = file.type;
30773         
30774         var _this = this;
30775         
30776         if(this.fireEvent('prepare', this, this.file) != false){
30777             
30778             var reader = new FileReader();
30779             
30780             reader.onload = function (e) {
30781                 if (e.target.error) {
30782                     Roo.log(e.target.error);
30783                     return;
30784                 }
30785                 
30786                 var buffer = e.target.result,
30787                     dataView = new DataView(buffer),
30788                     offset = 2,
30789                     maxOffset = dataView.byteLength - 4,
30790                     markerBytes,
30791                     markerLength;
30792                 
30793                 if (dataView.getUint16(0) === 0xffd8) {
30794                     while (offset < maxOffset) {
30795                         markerBytes = dataView.getUint16(offset);
30796                         
30797                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30798                             markerLength = dataView.getUint16(offset + 2) + 2;
30799                             if (offset + markerLength > dataView.byteLength) {
30800                                 Roo.log('Invalid meta data: Invalid segment size.');
30801                                 break;
30802                             }
30803                             
30804                             if(markerBytes == 0xffe1){
30805                                 _this.parseExifData(
30806                                     dataView,
30807                                     offset,
30808                                     markerLength
30809                                 );
30810                             }
30811                             
30812                             offset += markerLength;
30813                             
30814                             continue;
30815                         }
30816                         
30817                         break;
30818                     }
30819                     
30820                 }
30821                 
30822                 var url = _this.urlAPI.createObjectURL(_this.file);
30823                 
30824                 _this.loadCanvas(url);
30825                 
30826                 return;
30827             }
30828             
30829             reader.readAsArrayBuffer(this.file);
30830             
30831         }
30832         
30833     },
30834     
30835     parseExifData : function(dataView, offset, length)
30836     {
30837         var tiffOffset = offset + 10,
30838             littleEndian,
30839             dirOffset;
30840     
30841         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30842             // No Exif data, might be XMP data instead
30843             return;
30844         }
30845         
30846         // Check for the ASCII code for "Exif" (0x45786966):
30847         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30848             // No Exif data, might be XMP data instead
30849             return;
30850         }
30851         if (tiffOffset + 8 > dataView.byteLength) {
30852             Roo.log('Invalid Exif data: Invalid segment size.');
30853             return;
30854         }
30855         // Check for the two null bytes:
30856         if (dataView.getUint16(offset + 8) !== 0x0000) {
30857             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30858             return;
30859         }
30860         // Check the byte alignment:
30861         switch (dataView.getUint16(tiffOffset)) {
30862         case 0x4949:
30863             littleEndian = true;
30864             break;
30865         case 0x4D4D:
30866             littleEndian = false;
30867             break;
30868         default:
30869             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30870             return;
30871         }
30872         // Check for the TIFF tag marker (0x002A):
30873         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30874             Roo.log('Invalid Exif data: Missing TIFF marker.');
30875             return;
30876         }
30877         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30878         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30879         
30880         this.parseExifTags(
30881             dataView,
30882             tiffOffset,
30883             tiffOffset + dirOffset,
30884             littleEndian
30885         );
30886     },
30887     
30888     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30889     {
30890         var tagsNumber,
30891             dirEndOffset,
30892             i;
30893         if (dirOffset + 6 > dataView.byteLength) {
30894             Roo.log('Invalid Exif data: Invalid directory offset.');
30895             return;
30896         }
30897         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30898         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30899         if (dirEndOffset + 4 > dataView.byteLength) {
30900             Roo.log('Invalid Exif data: Invalid directory size.');
30901             return;
30902         }
30903         for (i = 0; i < tagsNumber; i += 1) {
30904             this.parseExifTag(
30905                 dataView,
30906                 tiffOffset,
30907                 dirOffset + 2 + 12 * i, // tag offset
30908                 littleEndian
30909             );
30910         }
30911         // Return the offset to the next directory:
30912         return dataView.getUint32(dirEndOffset, littleEndian);
30913     },
30914     
30915     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30916     {
30917         var tag = dataView.getUint16(offset, littleEndian);
30918         
30919         this.exif[tag] = this.getExifValue(
30920             dataView,
30921             tiffOffset,
30922             offset,
30923             dataView.getUint16(offset + 2, littleEndian), // tag type
30924             dataView.getUint32(offset + 4, littleEndian), // tag length
30925             littleEndian
30926         );
30927     },
30928     
30929     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30930     {
30931         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30932             tagSize,
30933             dataOffset,
30934             values,
30935             i,
30936             str,
30937             c;
30938     
30939         if (!tagType) {
30940             Roo.log('Invalid Exif data: Invalid tag type.');
30941             return;
30942         }
30943         
30944         tagSize = tagType.size * length;
30945         // Determine if the value is contained in the dataOffset bytes,
30946         // or if the value at the dataOffset is a pointer to the actual data:
30947         dataOffset = tagSize > 4 ?
30948                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30949         if (dataOffset + tagSize > dataView.byteLength) {
30950             Roo.log('Invalid Exif data: Invalid data offset.');
30951             return;
30952         }
30953         if (length === 1) {
30954             return tagType.getValue(dataView, dataOffset, littleEndian);
30955         }
30956         values = [];
30957         for (i = 0; i < length; i += 1) {
30958             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30959         }
30960         
30961         if (tagType.ascii) {
30962             str = '';
30963             // Concatenate the chars:
30964             for (i = 0; i < values.length; i += 1) {
30965                 c = values[i];
30966                 // Ignore the terminating NULL byte(s):
30967                 if (c === '\u0000') {
30968                     break;
30969                 }
30970                 str += c;
30971             }
30972             return str;
30973         }
30974         return values;
30975     }
30976     
30977 });
30978
30979 Roo.apply(Roo.bootstrap.UploadCropbox, {
30980     tags : {
30981         'Orientation': 0x0112
30982     },
30983     
30984     Orientation: {
30985             1: 0, //'top-left',
30986 //            2: 'top-right',
30987             3: 180, //'bottom-right',
30988 //            4: 'bottom-left',
30989 //            5: 'left-top',
30990             6: 90, //'right-top',
30991 //            7: 'right-bottom',
30992             8: 270 //'left-bottom'
30993     },
30994     
30995     exifTagTypes : {
30996         // byte, 8-bit unsigned int:
30997         1: {
30998             getValue: function (dataView, dataOffset) {
30999                 return dataView.getUint8(dataOffset);
31000             },
31001             size: 1
31002         },
31003         // ascii, 8-bit byte:
31004         2: {
31005             getValue: function (dataView, dataOffset) {
31006                 return String.fromCharCode(dataView.getUint8(dataOffset));
31007             },
31008             size: 1,
31009             ascii: true
31010         },
31011         // short, 16 bit int:
31012         3: {
31013             getValue: function (dataView, dataOffset, littleEndian) {
31014                 return dataView.getUint16(dataOffset, littleEndian);
31015             },
31016             size: 2
31017         },
31018         // long, 32 bit int:
31019         4: {
31020             getValue: function (dataView, dataOffset, littleEndian) {
31021                 return dataView.getUint32(dataOffset, littleEndian);
31022             },
31023             size: 4
31024         },
31025         // rational = two long values, first is numerator, second is denominator:
31026         5: {
31027             getValue: function (dataView, dataOffset, littleEndian) {
31028                 return dataView.getUint32(dataOffset, littleEndian) /
31029                     dataView.getUint32(dataOffset + 4, littleEndian);
31030             },
31031             size: 8
31032         },
31033         // slong, 32 bit signed int:
31034         9: {
31035             getValue: function (dataView, dataOffset, littleEndian) {
31036                 return dataView.getInt32(dataOffset, littleEndian);
31037             },
31038             size: 4
31039         },
31040         // srational, two slongs, first is numerator, second is denominator:
31041         10: {
31042             getValue: function (dataView, dataOffset, littleEndian) {
31043                 return dataView.getInt32(dataOffset, littleEndian) /
31044                     dataView.getInt32(dataOffset + 4, littleEndian);
31045             },
31046             size: 8
31047         }
31048     },
31049     
31050     footer : {
31051         STANDARD : [
31052             {
31053                 tag : 'div',
31054                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31055                 action : 'rotate-left',
31056                 cn : [
31057                     {
31058                         tag : 'button',
31059                         cls : 'btn btn-default',
31060                         html : '<i class="fa fa-undo"></i>'
31061                     }
31062                 ]
31063             },
31064             {
31065                 tag : 'div',
31066                 cls : 'btn-group roo-upload-cropbox-picture',
31067                 action : 'picture',
31068                 cn : [
31069                     {
31070                         tag : 'button',
31071                         cls : 'btn btn-default',
31072                         html : '<i class="fa fa-picture-o"></i>'
31073                     }
31074                 ]
31075             },
31076             {
31077                 tag : 'div',
31078                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31079                 action : 'rotate-right',
31080                 cn : [
31081                     {
31082                         tag : 'button',
31083                         cls : 'btn btn-default',
31084                         html : '<i class="fa fa-repeat"></i>'
31085                     }
31086                 ]
31087             }
31088         ],
31089         DOCUMENT : [
31090             {
31091                 tag : 'div',
31092                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31093                 action : 'rotate-left',
31094                 cn : [
31095                     {
31096                         tag : 'button',
31097                         cls : 'btn btn-default',
31098                         html : '<i class="fa fa-undo"></i>'
31099                     }
31100                 ]
31101             },
31102             {
31103                 tag : 'div',
31104                 cls : 'btn-group roo-upload-cropbox-download',
31105                 action : 'download',
31106                 cn : [
31107                     {
31108                         tag : 'button',
31109                         cls : 'btn btn-default',
31110                         html : '<i class="fa fa-download"></i>'
31111                     }
31112                 ]
31113             },
31114             {
31115                 tag : 'div',
31116                 cls : 'btn-group roo-upload-cropbox-crop',
31117                 action : 'crop',
31118                 cn : [
31119                     {
31120                         tag : 'button',
31121                         cls : 'btn btn-default',
31122                         html : '<i class="fa fa-crop"></i>'
31123                     }
31124                 ]
31125             },
31126             {
31127                 tag : 'div',
31128                 cls : 'btn-group roo-upload-cropbox-trash',
31129                 action : 'trash',
31130                 cn : [
31131                     {
31132                         tag : 'button',
31133                         cls : 'btn btn-default',
31134                         html : '<i class="fa fa-trash"></i>'
31135                     }
31136                 ]
31137             },
31138             {
31139                 tag : 'div',
31140                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31141                 action : 'rotate-right',
31142                 cn : [
31143                     {
31144                         tag : 'button',
31145                         cls : 'btn btn-default',
31146                         html : '<i class="fa fa-repeat"></i>'
31147                     }
31148                 ]
31149             }
31150         ],
31151         ROTATOR : [
31152             {
31153                 tag : 'div',
31154                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31155                 action : 'rotate-left',
31156                 cn : [
31157                     {
31158                         tag : 'button',
31159                         cls : 'btn btn-default',
31160                         html : '<i class="fa fa-undo"></i>'
31161                     }
31162                 ]
31163             },
31164             {
31165                 tag : 'div',
31166                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31167                 action : 'rotate-right',
31168                 cn : [
31169                     {
31170                         tag : 'button',
31171                         cls : 'btn btn-default',
31172                         html : '<i class="fa fa-repeat"></i>'
31173                     }
31174                 ]
31175             }
31176         ]
31177     }
31178 });
31179
31180 /*
31181 * Licence: LGPL
31182 */
31183
31184 /**
31185  * @class Roo.bootstrap.DocumentManager
31186  * @extends Roo.bootstrap.Component
31187  * Bootstrap DocumentManager class
31188  * @cfg {String} paramName default 'imageUpload'
31189  * @cfg {String} toolTipName default 'filename'
31190  * @cfg {String} method default POST
31191  * @cfg {String} url action url
31192  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31193  * @cfg {Boolean} multiple multiple upload default true
31194  * @cfg {Number} thumbSize default 300
31195  * @cfg {String} fieldLabel
31196  * @cfg {Number} labelWidth default 4
31197  * @cfg {String} labelAlign (left|top) default left
31198  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31199 * @cfg {Number} labellg set the width of label (1-12)
31200  * @cfg {Number} labelmd set the width of label (1-12)
31201  * @cfg {Number} labelsm set the width of label (1-12)
31202  * @cfg {Number} labelxs set the width of label (1-12)
31203  * 
31204  * @constructor
31205  * Create a new DocumentManager
31206  * @param {Object} config The config object
31207  */
31208
31209 Roo.bootstrap.DocumentManager = function(config){
31210     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31211     
31212     this.files = [];
31213     this.delegates = [];
31214     
31215     this.addEvents({
31216         /**
31217          * @event initial
31218          * Fire when initial the DocumentManager
31219          * @param {Roo.bootstrap.DocumentManager} this
31220          */
31221         "initial" : true,
31222         /**
31223          * @event inspect
31224          * inspect selected file
31225          * @param {Roo.bootstrap.DocumentManager} this
31226          * @param {File} file
31227          */
31228         "inspect" : true,
31229         /**
31230          * @event exception
31231          * Fire when xhr load exception
31232          * @param {Roo.bootstrap.DocumentManager} this
31233          * @param {XMLHttpRequest} xhr
31234          */
31235         "exception" : true,
31236         /**
31237          * @event afterupload
31238          * Fire when xhr load exception
31239          * @param {Roo.bootstrap.DocumentManager} this
31240          * @param {XMLHttpRequest} xhr
31241          */
31242         "afterupload" : true,
31243         /**
31244          * @event prepare
31245          * prepare the form data
31246          * @param {Roo.bootstrap.DocumentManager} this
31247          * @param {Object} formData
31248          */
31249         "prepare" : true,
31250         /**
31251          * @event remove
31252          * Fire when remove the file
31253          * @param {Roo.bootstrap.DocumentManager} this
31254          * @param {Object} file
31255          */
31256         "remove" : true,
31257         /**
31258          * @event refresh
31259          * Fire after refresh the file
31260          * @param {Roo.bootstrap.DocumentManager} this
31261          */
31262         "refresh" : true,
31263         /**
31264          * @event click
31265          * Fire after click the image
31266          * @param {Roo.bootstrap.DocumentManager} this
31267          * @param {Object} file
31268          */
31269         "click" : true,
31270         /**
31271          * @event edit
31272          * Fire when upload a image and editable set to true
31273          * @param {Roo.bootstrap.DocumentManager} this
31274          * @param {Object} file
31275          */
31276         "edit" : true,
31277         /**
31278          * @event beforeselectfile
31279          * Fire before select file
31280          * @param {Roo.bootstrap.DocumentManager} this
31281          */
31282         "beforeselectfile" : true,
31283         /**
31284          * @event process
31285          * Fire before process file
31286          * @param {Roo.bootstrap.DocumentManager} this
31287          * @param {Object} file
31288          */
31289         "process" : true,
31290         /**
31291          * @event previewrendered
31292          * Fire when preview rendered
31293          * @param {Roo.bootstrap.DocumentManager} this
31294          * @param {Object} file
31295          */
31296         "previewrendered" : true,
31297         /**
31298          */
31299         "previewResize" : true
31300         
31301     });
31302 };
31303
31304 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31305     
31306     boxes : 0,
31307     inputName : '',
31308     thumbSize : 300,
31309     multiple : true,
31310     files : false,
31311     method : 'POST',
31312     url : '',
31313     paramName : 'imageUpload',
31314     toolTipName : 'filename',
31315     fieldLabel : '',
31316     labelWidth : 4,
31317     labelAlign : 'left',
31318     editable : true,
31319     delegates : false,
31320     xhr : false, 
31321     
31322     labellg : 0,
31323     labelmd : 0,
31324     labelsm : 0,
31325     labelxs : 0,
31326     
31327     getAutoCreate : function()
31328     {   
31329         var managerWidget = {
31330             tag : 'div',
31331             cls : 'roo-document-manager',
31332             cn : [
31333                 {
31334                     tag : 'input',
31335                     cls : 'roo-document-manager-selector',
31336                     type : 'file'
31337                 },
31338                 {
31339                     tag : 'div',
31340                     cls : 'roo-document-manager-uploader',
31341                     cn : [
31342                         {
31343                             tag : 'div',
31344                             cls : 'roo-document-manager-upload-btn',
31345                             html : '<i class="fa fa-plus"></i>'
31346                         }
31347                     ]
31348                     
31349                 }
31350             ]
31351         };
31352         
31353         var content = [
31354             {
31355                 tag : 'div',
31356                 cls : 'column col-md-12',
31357                 cn : managerWidget
31358             }
31359         ];
31360         
31361         if(this.fieldLabel.length){
31362             
31363             content = [
31364                 {
31365                     tag : 'div',
31366                     cls : 'column col-md-12',
31367                     html : this.fieldLabel
31368                 },
31369                 {
31370                     tag : 'div',
31371                     cls : 'column col-md-12',
31372                     cn : managerWidget
31373                 }
31374             ];
31375
31376             if(this.labelAlign == 'left'){
31377                 content = [
31378                     {
31379                         tag : 'div',
31380                         cls : 'column',
31381                         html : this.fieldLabel
31382                     },
31383                     {
31384                         tag : 'div',
31385                         cls : 'column',
31386                         cn : managerWidget
31387                     }
31388                 ];
31389                 
31390                 if(this.labelWidth > 12){
31391                     content[0].style = "width: " + this.labelWidth + 'px';
31392                 }
31393
31394                 if(this.labelWidth < 13 && this.labelmd == 0){
31395                     this.labelmd = this.labelWidth;
31396                 }
31397
31398                 if(this.labellg > 0){
31399                     content[0].cls += ' col-lg-' + this.labellg;
31400                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31401                 }
31402
31403                 if(this.labelmd > 0){
31404                     content[0].cls += ' col-md-' + this.labelmd;
31405                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31406                 }
31407
31408                 if(this.labelsm > 0){
31409                     content[0].cls += ' col-sm-' + this.labelsm;
31410                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31411                 }
31412
31413                 if(this.labelxs > 0){
31414                     content[0].cls += ' col-xs-' + this.labelxs;
31415                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31416                 }
31417                 
31418             }
31419         }
31420         
31421         var cfg = {
31422             tag : 'div',
31423             cls : 'row clearfix',
31424             cn : content
31425         };
31426         
31427         return cfg;
31428         
31429     },
31430     
31431     initEvents : function()
31432     {
31433         this.managerEl = this.el.select('.roo-document-manager', true).first();
31434         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31435         
31436         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31437         this.selectorEl.hide();
31438         
31439         if(this.multiple){
31440             this.selectorEl.attr('multiple', 'multiple');
31441         }
31442         
31443         this.selectorEl.on('change', this.onFileSelected, this);
31444         
31445         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31446         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31447         
31448         this.uploader.on('click', this.onUploaderClick, this);
31449         
31450         this.renderProgressDialog();
31451         
31452         var _this = this;
31453         
31454         window.addEventListener("resize", function() { _this.refresh(); } );
31455         
31456         this.fireEvent('initial', this);
31457     },
31458     
31459     renderProgressDialog : function()
31460     {
31461         var _this = this;
31462         
31463         this.progressDialog = new Roo.bootstrap.Modal({
31464             cls : 'roo-document-manager-progress-dialog',
31465             allow_close : false,
31466             animate : false,
31467             title : '',
31468             buttons : [
31469                 {
31470                     name  :'cancel',
31471                     weight : 'danger',
31472                     html : 'Cancel'
31473                 }
31474             ], 
31475             listeners : { 
31476                 btnclick : function() {
31477                     _this.uploadCancel();
31478                     this.hide();
31479                 }
31480             }
31481         });
31482          
31483         this.progressDialog.render(Roo.get(document.body));
31484          
31485         this.progress = new Roo.bootstrap.Progress({
31486             cls : 'roo-document-manager-progress',
31487             active : true,
31488             striped : true
31489         });
31490         
31491         this.progress.render(this.progressDialog.getChildContainer());
31492         
31493         this.progressBar = new Roo.bootstrap.ProgressBar({
31494             cls : 'roo-document-manager-progress-bar',
31495             aria_valuenow : 0,
31496             aria_valuemin : 0,
31497             aria_valuemax : 12,
31498             panel : 'success'
31499         });
31500         
31501         this.progressBar.render(this.progress.getChildContainer());
31502     },
31503     
31504     onUploaderClick : function(e)
31505     {
31506         e.preventDefault();
31507      
31508         if(this.fireEvent('beforeselectfile', this) != false){
31509             this.selectorEl.dom.click();
31510         }
31511         
31512     },
31513     
31514     onFileSelected : function(e)
31515     {
31516         e.preventDefault();
31517         
31518         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31519             return;
31520         }
31521         
31522         Roo.each(this.selectorEl.dom.files, function(file){
31523             if(this.fireEvent('inspect', this, file) != false){
31524                 this.files.push(file);
31525             }
31526         }, this);
31527         
31528         this.queue();
31529         
31530     },
31531     
31532     queue : function()
31533     {
31534         this.selectorEl.dom.value = '';
31535         
31536         if(!this.files || !this.files.length){
31537             return;
31538         }
31539         
31540         if(this.boxes > 0 && this.files.length > this.boxes){
31541             this.files = this.files.slice(0, this.boxes);
31542         }
31543         
31544         this.uploader.show();
31545         
31546         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31547             this.uploader.hide();
31548         }
31549         
31550         var _this = this;
31551         
31552         var files = [];
31553         
31554         var docs = [];
31555         
31556         Roo.each(this.files, function(file){
31557             
31558             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31559                 var f = this.renderPreview(file);
31560                 files.push(f);
31561                 return;
31562             }
31563             
31564             if(file.type.indexOf('image') != -1){
31565                 this.delegates.push(
31566                     (function(){
31567                         _this.process(file);
31568                     }).createDelegate(this)
31569                 );
31570         
31571                 return;
31572             }
31573             
31574             docs.push(
31575                 (function(){
31576                     _this.process(file);
31577                 }).createDelegate(this)
31578             );
31579             
31580         }, this);
31581         
31582         this.files = files;
31583         
31584         this.delegates = this.delegates.concat(docs);
31585         
31586         if(!this.delegates.length){
31587             this.refresh();
31588             return;
31589         }
31590         
31591         this.progressBar.aria_valuemax = this.delegates.length;
31592         
31593         this.arrange();
31594         
31595         return;
31596     },
31597     
31598     arrange : function()
31599     {
31600         if(!this.delegates.length){
31601             this.progressDialog.hide();
31602             this.refresh();
31603             return;
31604         }
31605         
31606         var delegate = this.delegates.shift();
31607         
31608         this.progressDialog.show();
31609         
31610         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31611         
31612         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31613         
31614         delegate();
31615     },
31616     
31617     refresh : function()
31618     {
31619         this.uploader.show();
31620         
31621         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31622             this.uploader.hide();
31623         }
31624         
31625         Roo.isTouch ? this.closable(false) : this.closable(true);
31626         
31627         this.fireEvent('refresh', this);
31628     },
31629     
31630     onRemove : function(e, el, o)
31631     {
31632         e.preventDefault();
31633         
31634         this.fireEvent('remove', this, o);
31635         
31636     },
31637     
31638     remove : function(o)
31639     {
31640         var files = [];
31641         
31642         Roo.each(this.files, function(file){
31643             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31644                 files.push(file);
31645                 return;
31646             }
31647
31648             o.target.remove();
31649
31650         }, this);
31651         
31652         this.files = files;
31653         
31654         this.refresh();
31655     },
31656     
31657     clear : function()
31658     {
31659         Roo.each(this.files, function(file){
31660             if(!file.target){
31661                 return;
31662             }
31663             
31664             file.target.remove();
31665
31666         }, this);
31667         
31668         this.files = [];
31669         
31670         this.refresh();
31671     },
31672     
31673     onClick : function(e, el, o)
31674     {
31675         e.preventDefault();
31676         
31677         this.fireEvent('click', this, o);
31678         
31679     },
31680     
31681     closable : function(closable)
31682     {
31683         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31684             
31685             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31686             
31687             if(closable){
31688                 el.show();
31689                 return;
31690             }
31691             
31692             el.hide();
31693             
31694         }, this);
31695     },
31696     
31697     xhrOnLoad : function(xhr)
31698     {
31699         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31700             el.remove();
31701         }, this);
31702         
31703         if (xhr.readyState !== 4) {
31704             this.arrange();
31705             this.fireEvent('exception', this, xhr);
31706             return;
31707         }
31708
31709         var response = Roo.decode(xhr.responseText);
31710         
31711         if(!response.success){
31712             this.arrange();
31713             this.fireEvent('exception', this, xhr);
31714             return;
31715         }
31716         
31717         var file = this.renderPreview(response.data);
31718         
31719         this.files.push(file);
31720         
31721         this.arrange();
31722         
31723         this.fireEvent('afterupload', this, xhr);
31724         
31725     },
31726     
31727     xhrOnError : function(xhr)
31728     {
31729         Roo.log('xhr on error');
31730         
31731         var response = Roo.decode(xhr.responseText);
31732           
31733         Roo.log(response);
31734         
31735         this.arrange();
31736     },
31737     
31738     process : function(file)
31739     {
31740         if(this.fireEvent('process', this, file) !== false){
31741             if(this.editable && file.type.indexOf('image') != -1){
31742                 this.fireEvent('edit', this, file);
31743                 return;
31744             }
31745
31746             this.uploadStart(file, false);
31747
31748             return;
31749         }
31750         
31751     },
31752     
31753     uploadStart : function(file, crop)
31754     {
31755         this.xhr = new XMLHttpRequest();
31756         
31757         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31758             this.arrange();
31759             return;
31760         }
31761         
31762         file.xhr = this.xhr;
31763             
31764         this.managerEl.createChild({
31765             tag : 'div',
31766             cls : 'roo-document-manager-loading',
31767             cn : [
31768                 {
31769                     tag : 'div',
31770                     tooltip : file.name,
31771                     cls : 'roo-document-manager-thumb',
31772                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31773                 }
31774             ]
31775
31776         });
31777
31778         this.xhr.open(this.method, this.url, true);
31779         
31780         var headers = {
31781             "Accept": "application/json",
31782             "Cache-Control": "no-cache",
31783             "X-Requested-With": "XMLHttpRequest"
31784         };
31785         
31786         for (var headerName in headers) {
31787             var headerValue = headers[headerName];
31788             if (headerValue) {
31789                 this.xhr.setRequestHeader(headerName, headerValue);
31790             }
31791         }
31792         
31793         var _this = this;
31794         
31795         this.xhr.onload = function()
31796         {
31797             _this.xhrOnLoad(_this.xhr);
31798         }
31799         
31800         this.xhr.onerror = function()
31801         {
31802             _this.xhrOnError(_this.xhr);
31803         }
31804         
31805         var formData = new FormData();
31806
31807         formData.append('returnHTML', 'NO');
31808         
31809         if(crop){
31810             formData.append('crop', crop);
31811         }
31812         
31813         formData.append(this.paramName, file, file.name);
31814         
31815         var options = {
31816             file : file, 
31817             manually : false
31818         };
31819         
31820         if(this.fireEvent('prepare', this, formData, options) != false){
31821             
31822             if(options.manually){
31823                 return;
31824             }
31825             
31826             this.xhr.send(formData);
31827             return;
31828         };
31829         
31830         this.uploadCancel();
31831     },
31832     
31833     uploadCancel : function()
31834     {
31835         if (this.xhr) {
31836             this.xhr.abort();
31837         }
31838         
31839         this.delegates = [];
31840         
31841         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31842             el.remove();
31843         }, this);
31844         
31845         this.arrange();
31846     },
31847     
31848     renderPreview : function(file)
31849     {
31850         if(typeof(file.target) != 'undefined' && file.target){
31851             return file;
31852         }
31853         
31854         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31855         
31856         var previewEl = this.managerEl.createChild({
31857             tag : 'div',
31858             cls : 'roo-document-manager-preview',
31859             cn : [
31860                 {
31861                     tag : 'div',
31862                     tooltip : file[this.toolTipName],
31863                     cls : 'roo-document-manager-thumb',
31864                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31865                 },
31866                 {
31867                     tag : 'button',
31868                     cls : 'close',
31869                     html : '<i class="fa fa-times-circle"></i>'
31870                 }
31871             ]
31872         });
31873
31874         var close = previewEl.select('button.close', true).first();
31875
31876         close.on('click', this.onRemove, this, file);
31877
31878         file.target = previewEl;
31879
31880         var image = previewEl.select('img', true).first();
31881         
31882         var _this = this;
31883         
31884         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31885         
31886         image.on('click', this.onClick, this, file);
31887         
31888         this.fireEvent('previewrendered', this, file);
31889         
31890         return file;
31891         
31892     },
31893     
31894     onPreviewLoad : function(file, image)
31895     {
31896         if(typeof(file.target) == 'undefined' || !file.target){
31897             return;
31898         }
31899         
31900         var width = image.dom.naturalWidth || image.dom.width;
31901         var height = image.dom.naturalHeight || image.dom.height;
31902         
31903         if(!this.previewResize) {
31904             return;
31905         }
31906         
31907         if(width > height){
31908             file.target.addClass('wide');
31909             return;
31910         }
31911         
31912         file.target.addClass('tall');
31913         return;
31914         
31915     },
31916     
31917     uploadFromSource : function(file, crop)
31918     {
31919         this.xhr = new XMLHttpRequest();
31920         
31921         this.managerEl.createChild({
31922             tag : 'div',
31923             cls : 'roo-document-manager-loading',
31924             cn : [
31925                 {
31926                     tag : 'div',
31927                     tooltip : file.name,
31928                     cls : 'roo-document-manager-thumb',
31929                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31930                 }
31931             ]
31932
31933         });
31934
31935         this.xhr.open(this.method, this.url, true);
31936         
31937         var headers = {
31938             "Accept": "application/json",
31939             "Cache-Control": "no-cache",
31940             "X-Requested-With": "XMLHttpRequest"
31941         };
31942         
31943         for (var headerName in headers) {
31944             var headerValue = headers[headerName];
31945             if (headerValue) {
31946                 this.xhr.setRequestHeader(headerName, headerValue);
31947             }
31948         }
31949         
31950         var _this = this;
31951         
31952         this.xhr.onload = function()
31953         {
31954             _this.xhrOnLoad(_this.xhr);
31955         }
31956         
31957         this.xhr.onerror = function()
31958         {
31959             _this.xhrOnError(_this.xhr);
31960         }
31961         
31962         var formData = new FormData();
31963
31964         formData.append('returnHTML', 'NO');
31965         
31966         formData.append('crop', crop);
31967         
31968         if(typeof(file.filename) != 'undefined'){
31969             formData.append('filename', file.filename);
31970         }
31971         
31972         if(typeof(file.mimetype) != 'undefined'){
31973             formData.append('mimetype', file.mimetype);
31974         }
31975         
31976         Roo.log(formData);
31977         
31978         if(this.fireEvent('prepare', this, formData) != false){
31979             this.xhr.send(formData);
31980         };
31981     }
31982 });
31983
31984 /*
31985 * Licence: LGPL
31986 */
31987
31988 /**
31989  * @class Roo.bootstrap.DocumentViewer
31990  * @extends Roo.bootstrap.Component
31991  * Bootstrap DocumentViewer class
31992  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31993  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31994  * 
31995  * @constructor
31996  * Create a new DocumentViewer
31997  * @param {Object} config The config object
31998  */
31999
32000 Roo.bootstrap.DocumentViewer = function(config){
32001     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32002     
32003     this.addEvents({
32004         /**
32005          * @event initial
32006          * Fire after initEvent
32007          * @param {Roo.bootstrap.DocumentViewer} this
32008          */
32009         "initial" : true,
32010         /**
32011          * @event click
32012          * Fire after click
32013          * @param {Roo.bootstrap.DocumentViewer} this
32014          */
32015         "click" : true,
32016         /**
32017          * @event download
32018          * Fire after download button
32019          * @param {Roo.bootstrap.DocumentViewer} this
32020          */
32021         "download" : true,
32022         /**
32023          * @event trash
32024          * Fire after trash button
32025          * @param {Roo.bootstrap.DocumentViewer} this
32026          */
32027         "trash" : true
32028         
32029     });
32030 };
32031
32032 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32033     
32034     showDownload : true,
32035     
32036     showTrash : true,
32037     
32038     getAutoCreate : function()
32039     {
32040         var cfg = {
32041             tag : 'div',
32042             cls : 'roo-document-viewer',
32043             cn : [
32044                 {
32045                     tag : 'div',
32046                     cls : 'roo-document-viewer-body',
32047                     cn : [
32048                         {
32049                             tag : 'div',
32050                             cls : 'roo-document-viewer-thumb',
32051                             cn : [
32052                                 {
32053                                     tag : 'img',
32054                                     cls : 'roo-document-viewer-image'
32055                                 }
32056                             ]
32057                         }
32058                     ]
32059                 },
32060                 {
32061                     tag : 'div',
32062                     cls : 'roo-document-viewer-footer',
32063                     cn : {
32064                         tag : 'div',
32065                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32066                         cn : [
32067                             {
32068                                 tag : 'div',
32069                                 cls : 'btn-group roo-document-viewer-download',
32070                                 cn : [
32071                                     {
32072                                         tag : 'button',
32073                                         cls : 'btn btn-default',
32074                                         html : '<i class="fa fa-download"></i>'
32075                                     }
32076                                 ]
32077                             },
32078                             {
32079                                 tag : 'div',
32080                                 cls : 'btn-group roo-document-viewer-trash',
32081                                 cn : [
32082                                     {
32083                                         tag : 'button',
32084                                         cls : 'btn btn-default',
32085                                         html : '<i class="fa fa-trash"></i>'
32086                                     }
32087                                 ]
32088                             }
32089                         ]
32090                     }
32091                 }
32092             ]
32093         };
32094         
32095         return cfg;
32096     },
32097     
32098     initEvents : function()
32099     {
32100         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32101         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32102         
32103         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32104         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32105         
32106         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32107         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32108         
32109         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32110         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32111         
32112         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32113         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32114         
32115         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32116         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32117         
32118         this.bodyEl.on('click', this.onClick, this);
32119         this.downloadBtn.on('click', this.onDownload, this);
32120         this.trashBtn.on('click', this.onTrash, this);
32121         
32122         this.downloadBtn.hide();
32123         this.trashBtn.hide();
32124         
32125         if(this.showDownload){
32126             this.downloadBtn.show();
32127         }
32128         
32129         if(this.showTrash){
32130             this.trashBtn.show();
32131         }
32132         
32133         if(!this.showDownload && !this.showTrash) {
32134             this.footerEl.hide();
32135         }
32136         
32137     },
32138     
32139     initial : function()
32140     {
32141         this.fireEvent('initial', this);
32142         
32143     },
32144     
32145     onClick : function(e)
32146     {
32147         e.preventDefault();
32148         
32149         this.fireEvent('click', this);
32150     },
32151     
32152     onDownload : function(e)
32153     {
32154         e.preventDefault();
32155         
32156         this.fireEvent('download', this);
32157     },
32158     
32159     onTrash : function(e)
32160     {
32161         e.preventDefault();
32162         
32163         this.fireEvent('trash', this);
32164     }
32165     
32166 });
32167 /*
32168  * - LGPL
32169  *
32170  * nav progress bar
32171  * 
32172  */
32173
32174 /**
32175  * @class Roo.bootstrap.NavProgressBar
32176  * @extends Roo.bootstrap.Component
32177  * Bootstrap NavProgressBar class
32178  * 
32179  * @constructor
32180  * Create a new nav progress bar
32181  * @param {Object} config The config object
32182  */
32183
32184 Roo.bootstrap.NavProgressBar = function(config){
32185     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32186
32187     this.bullets = this.bullets || [];
32188    
32189 //    Roo.bootstrap.NavProgressBar.register(this);
32190      this.addEvents({
32191         /**
32192              * @event changed
32193              * Fires when the active item changes
32194              * @param {Roo.bootstrap.NavProgressBar} this
32195              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32196              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32197          */
32198         'changed': true
32199      });
32200     
32201 };
32202
32203 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32204     
32205     bullets : [],
32206     barItems : [],
32207     
32208     getAutoCreate : function()
32209     {
32210         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32211         
32212         cfg = {
32213             tag : 'div',
32214             cls : 'roo-navigation-bar-group',
32215             cn : [
32216                 {
32217                     tag : 'div',
32218                     cls : 'roo-navigation-top-bar'
32219                 },
32220                 {
32221                     tag : 'div',
32222                     cls : 'roo-navigation-bullets-bar',
32223                     cn : [
32224                         {
32225                             tag : 'ul',
32226                             cls : 'roo-navigation-bar'
32227                         }
32228                     ]
32229                 },
32230                 
32231                 {
32232                     tag : 'div',
32233                     cls : 'roo-navigation-bottom-bar'
32234                 }
32235             ]
32236             
32237         };
32238         
32239         return cfg;
32240         
32241     },
32242     
32243     initEvents: function() 
32244     {
32245         
32246     },
32247     
32248     onRender : function(ct, position) 
32249     {
32250         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32251         
32252         if(this.bullets.length){
32253             Roo.each(this.bullets, function(b){
32254                this.addItem(b);
32255             }, this);
32256         }
32257         
32258         this.format();
32259         
32260     },
32261     
32262     addItem : function(cfg)
32263     {
32264         var item = new Roo.bootstrap.NavProgressItem(cfg);
32265         
32266         item.parentId = this.id;
32267         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32268         
32269         if(cfg.html){
32270             var top = new Roo.bootstrap.Element({
32271                 tag : 'div',
32272                 cls : 'roo-navigation-bar-text'
32273             });
32274             
32275             var bottom = new Roo.bootstrap.Element({
32276                 tag : 'div',
32277                 cls : 'roo-navigation-bar-text'
32278             });
32279             
32280             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32281             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32282             
32283             var topText = new Roo.bootstrap.Element({
32284                 tag : 'span',
32285                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32286             });
32287             
32288             var bottomText = new Roo.bootstrap.Element({
32289                 tag : 'span',
32290                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32291             });
32292             
32293             topText.onRender(top.el, null);
32294             bottomText.onRender(bottom.el, null);
32295             
32296             item.topEl = top;
32297             item.bottomEl = bottom;
32298         }
32299         
32300         this.barItems.push(item);
32301         
32302         return item;
32303     },
32304     
32305     getActive : function()
32306     {
32307         var active = false;
32308         
32309         Roo.each(this.barItems, function(v){
32310             
32311             if (!v.isActive()) {
32312                 return;
32313             }
32314             
32315             active = v;
32316             return false;
32317             
32318         });
32319         
32320         return active;
32321     },
32322     
32323     setActiveItem : function(item)
32324     {
32325         var prev = false;
32326         
32327         Roo.each(this.barItems, function(v){
32328             if (v.rid == item.rid) {
32329                 return ;
32330             }
32331             
32332             if (v.isActive()) {
32333                 v.setActive(false);
32334                 prev = v;
32335             }
32336         });
32337
32338         item.setActive(true);
32339         
32340         this.fireEvent('changed', this, item, prev);
32341     },
32342     
32343     getBarItem: function(rid)
32344     {
32345         var ret = false;
32346         
32347         Roo.each(this.barItems, function(e) {
32348             if (e.rid != rid) {
32349                 return;
32350             }
32351             
32352             ret =  e;
32353             return false;
32354         });
32355         
32356         return ret;
32357     },
32358     
32359     indexOfItem : function(item)
32360     {
32361         var index = false;
32362         
32363         Roo.each(this.barItems, function(v, i){
32364             
32365             if (v.rid != item.rid) {
32366                 return;
32367             }
32368             
32369             index = i;
32370             return false
32371         });
32372         
32373         return index;
32374     },
32375     
32376     setActiveNext : function()
32377     {
32378         var i = this.indexOfItem(this.getActive());
32379         
32380         if (i > this.barItems.length) {
32381             return;
32382         }
32383         
32384         this.setActiveItem(this.barItems[i+1]);
32385     },
32386     
32387     setActivePrev : function()
32388     {
32389         var i = this.indexOfItem(this.getActive());
32390         
32391         if (i  < 1) {
32392             return;
32393         }
32394         
32395         this.setActiveItem(this.barItems[i-1]);
32396     },
32397     
32398     format : function()
32399     {
32400         if(!this.barItems.length){
32401             return;
32402         }
32403      
32404         var width = 100 / this.barItems.length;
32405         
32406         Roo.each(this.barItems, function(i){
32407             i.el.setStyle('width', width + '%');
32408             i.topEl.el.setStyle('width', width + '%');
32409             i.bottomEl.el.setStyle('width', width + '%');
32410         }, this);
32411         
32412     }
32413     
32414 });
32415 /*
32416  * - LGPL
32417  *
32418  * Nav Progress Item
32419  * 
32420  */
32421
32422 /**
32423  * @class Roo.bootstrap.NavProgressItem
32424  * @extends Roo.bootstrap.Component
32425  * Bootstrap NavProgressItem class
32426  * @cfg {String} rid the reference id
32427  * @cfg {Boolean} active (true|false) Is item active default false
32428  * @cfg {Boolean} disabled (true|false) Is item active default false
32429  * @cfg {String} html
32430  * @cfg {String} position (top|bottom) text position default bottom
32431  * @cfg {String} icon show icon instead of number
32432  * 
32433  * @constructor
32434  * Create a new NavProgressItem
32435  * @param {Object} config The config object
32436  */
32437 Roo.bootstrap.NavProgressItem = function(config){
32438     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32439     this.addEvents({
32440         // raw events
32441         /**
32442          * @event click
32443          * The raw click event for the entire grid.
32444          * @param {Roo.bootstrap.NavProgressItem} this
32445          * @param {Roo.EventObject} e
32446          */
32447         "click" : true
32448     });
32449    
32450 };
32451
32452 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32453     
32454     rid : '',
32455     active : false,
32456     disabled : false,
32457     html : '',
32458     position : 'bottom',
32459     icon : false,
32460     
32461     getAutoCreate : function()
32462     {
32463         var iconCls = 'roo-navigation-bar-item-icon';
32464         
32465         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32466         
32467         var cfg = {
32468             tag: 'li',
32469             cls: 'roo-navigation-bar-item',
32470             cn : [
32471                 {
32472                     tag : 'i',
32473                     cls : iconCls
32474                 }
32475             ]
32476         };
32477         
32478         if(this.active){
32479             cfg.cls += ' active';
32480         }
32481         if(this.disabled){
32482             cfg.cls += ' disabled';
32483         }
32484         
32485         return cfg;
32486     },
32487     
32488     disable : function()
32489     {
32490         this.setDisabled(true);
32491     },
32492     
32493     enable : function()
32494     {
32495         this.setDisabled(false);
32496     },
32497     
32498     initEvents: function() 
32499     {
32500         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32501         
32502         this.iconEl.on('click', this.onClick, this);
32503     },
32504     
32505     onClick : function(e)
32506     {
32507         e.preventDefault();
32508         
32509         if(this.disabled){
32510             return;
32511         }
32512         
32513         if(this.fireEvent('click', this, e) === false){
32514             return;
32515         };
32516         
32517         this.parent().setActiveItem(this);
32518     },
32519     
32520     isActive: function () 
32521     {
32522         return this.active;
32523     },
32524     
32525     setActive : function(state)
32526     {
32527         if(this.active == state){
32528             return;
32529         }
32530         
32531         this.active = state;
32532         
32533         if (state) {
32534             this.el.addClass('active');
32535             return;
32536         }
32537         
32538         this.el.removeClass('active');
32539         
32540         return;
32541     },
32542     
32543     setDisabled : function(state)
32544     {
32545         if(this.disabled == state){
32546             return;
32547         }
32548         
32549         this.disabled = state;
32550         
32551         if (state) {
32552             this.el.addClass('disabled');
32553             return;
32554         }
32555         
32556         this.el.removeClass('disabled');
32557     },
32558     
32559     tooltipEl : function()
32560     {
32561         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32562     }
32563 });
32564  
32565
32566  /*
32567  * - LGPL
32568  *
32569  * FieldLabel
32570  * 
32571  */
32572
32573 /**
32574  * @class Roo.bootstrap.FieldLabel
32575  * @extends Roo.bootstrap.Component
32576  * Bootstrap FieldLabel class
32577  * @cfg {String} html contents of the element
32578  * @cfg {String} tag tag of the element default label
32579  * @cfg {String} cls class of the element
32580  * @cfg {String} target label target 
32581  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32582  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32583  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32584  * @cfg {String} iconTooltip default "This field is required"
32585  * @cfg {String} indicatorpos (left|right) default left
32586  * 
32587  * @constructor
32588  * Create a new FieldLabel
32589  * @param {Object} config The config object
32590  */
32591
32592 Roo.bootstrap.FieldLabel = function(config){
32593     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32594     
32595     this.addEvents({
32596             /**
32597              * @event invalid
32598              * Fires after the field has been marked as invalid.
32599              * @param {Roo.form.FieldLabel} this
32600              * @param {String} msg The validation message
32601              */
32602             invalid : true,
32603             /**
32604              * @event valid
32605              * Fires after the field has been validated with no errors.
32606              * @param {Roo.form.FieldLabel} this
32607              */
32608             valid : true
32609         });
32610 };
32611
32612 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32613     
32614     tag: 'label',
32615     cls: '',
32616     html: '',
32617     target: '',
32618     allowBlank : true,
32619     invalidClass : 'has-warning',
32620     validClass : 'has-success',
32621     iconTooltip : 'This field is required',
32622     indicatorpos : 'left',
32623     
32624     getAutoCreate : function(){
32625         
32626         var cls = "";
32627         if (!this.allowBlank) {
32628             cls  = "visible";
32629         }
32630         
32631         var cfg = {
32632             tag : this.tag,
32633             cls : 'roo-bootstrap-field-label ' + this.cls,
32634             for : this.target,
32635             cn : [
32636                 {
32637                     tag : 'i',
32638                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32639                     tooltip : this.iconTooltip
32640                 },
32641                 {
32642                     tag : 'span',
32643                     html : this.html
32644                 }
32645             ] 
32646         };
32647         
32648         if(this.indicatorpos == 'right'){
32649             var cfg = {
32650                 tag : this.tag,
32651                 cls : 'roo-bootstrap-field-label ' + this.cls,
32652                 for : this.target,
32653                 cn : [
32654                     {
32655                         tag : 'span',
32656                         html : this.html
32657                     },
32658                     {
32659                         tag : 'i',
32660                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32661                         tooltip : this.iconTooltip
32662                     }
32663                 ] 
32664             };
32665         }
32666         
32667         return cfg;
32668     },
32669     
32670     initEvents: function() 
32671     {
32672         Roo.bootstrap.Element.superclass.initEvents.call(this);
32673         
32674         this.indicator = this.indicatorEl();
32675         
32676         if(this.indicator){
32677             this.indicator.removeClass('visible');
32678             this.indicator.addClass('invisible');
32679         }
32680         
32681         Roo.bootstrap.FieldLabel.register(this);
32682     },
32683     
32684     indicatorEl : function()
32685     {
32686         var indicator = this.el.select('i.roo-required-indicator',true).first();
32687         
32688         if(!indicator){
32689             return false;
32690         }
32691         
32692         return indicator;
32693         
32694     },
32695     
32696     /**
32697      * Mark this field as valid
32698      */
32699     markValid : function()
32700     {
32701         if(this.indicator){
32702             this.indicator.removeClass('visible');
32703             this.indicator.addClass('invisible');
32704         }
32705         if (Roo.bootstrap.version == 3) {
32706             this.el.removeClass(this.invalidClass);
32707             this.el.addClass(this.validClass);
32708         } else {
32709             this.el.removeClass('is-invalid');
32710             this.el.addClass('is-valid');
32711         }
32712         
32713         
32714         this.fireEvent('valid', this);
32715     },
32716     
32717     /**
32718      * Mark this field as invalid
32719      * @param {String} msg The validation message
32720      */
32721     markInvalid : function(msg)
32722     {
32723         if(this.indicator){
32724             this.indicator.removeClass('invisible');
32725             this.indicator.addClass('visible');
32726         }
32727           if (Roo.bootstrap.version == 3) {
32728             this.el.removeClass(this.validClass);
32729             this.el.addClass(this.invalidClass);
32730         } else {
32731             this.el.removeClass('is-valid');
32732             this.el.addClass('is-invalid');
32733         }
32734         
32735         
32736         this.fireEvent('invalid', this, msg);
32737     }
32738     
32739    
32740 });
32741
32742 Roo.apply(Roo.bootstrap.FieldLabel, {
32743     
32744     groups: {},
32745     
32746      /**
32747     * register a FieldLabel Group
32748     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32749     */
32750     register : function(label)
32751     {
32752         if(this.groups.hasOwnProperty(label.target)){
32753             return;
32754         }
32755      
32756         this.groups[label.target] = label;
32757         
32758     },
32759     /**
32760     * fetch a FieldLabel Group based on the target
32761     * @param {string} target
32762     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32763     */
32764     get: function(target) {
32765         if (typeof(this.groups[target]) == 'undefined') {
32766             return false;
32767         }
32768         
32769         return this.groups[target] ;
32770     }
32771 });
32772
32773  
32774
32775  /*
32776  * - LGPL
32777  *
32778  * page DateSplitField.
32779  * 
32780  */
32781
32782
32783 /**
32784  * @class Roo.bootstrap.DateSplitField
32785  * @extends Roo.bootstrap.Component
32786  * Bootstrap DateSplitField class
32787  * @cfg {string} fieldLabel - the label associated
32788  * @cfg {Number} labelWidth set the width of label (0-12)
32789  * @cfg {String} labelAlign (top|left)
32790  * @cfg {Boolean} dayAllowBlank (true|false) default false
32791  * @cfg {Boolean} monthAllowBlank (true|false) default false
32792  * @cfg {Boolean} yearAllowBlank (true|false) default false
32793  * @cfg {string} dayPlaceholder 
32794  * @cfg {string} monthPlaceholder
32795  * @cfg {string} yearPlaceholder
32796  * @cfg {string} dayFormat default 'd'
32797  * @cfg {string} monthFormat default 'm'
32798  * @cfg {string} yearFormat default 'Y'
32799  * @cfg {Number} labellg set the width of label (1-12)
32800  * @cfg {Number} labelmd set the width of label (1-12)
32801  * @cfg {Number} labelsm set the width of label (1-12)
32802  * @cfg {Number} labelxs set the width of label (1-12)
32803
32804  *     
32805  * @constructor
32806  * Create a new DateSplitField
32807  * @param {Object} config The config object
32808  */
32809
32810 Roo.bootstrap.DateSplitField = function(config){
32811     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32812     
32813     this.addEvents({
32814         // raw events
32815          /**
32816          * @event years
32817          * getting the data of years
32818          * @param {Roo.bootstrap.DateSplitField} this
32819          * @param {Object} years
32820          */
32821         "years" : true,
32822         /**
32823          * @event days
32824          * getting the data of days
32825          * @param {Roo.bootstrap.DateSplitField} this
32826          * @param {Object} days
32827          */
32828         "days" : true,
32829         /**
32830          * @event invalid
32831          * Fires after the field has been marked as invalid.
32832          * @param {Roo.form.Field} this
32833          * @param {String} msg The validation message
32834          */
32835         invalid : true,
32836        /**
32837          * @event valid
32838          * Fires after the field has been validated with no errors.
32839          * @param {Roo.form.Field} this
32840          */
32841         valid : true
32842     });
32843 };
32844
32845 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32846     
32847     fieldLabel : '',
32848     labelAlign : 'top',
32849     labelWidth : 3,
32850     dayAllowBlank : false,
32851     monthAllowBlank : false,
32852     yearAllowBlank : false,
32853     dayPlaceholder : '',
32854     monthPlaceholder : '',
32855     yearPlaceholder : '',
32856     dayFormat : 'd',
32857     monthFormat : 'm',
32858     yearFormat : 'Y',
32859     isFormField : true,
32860     labellg : 0,
32861     labelmd : 0,
32862     labelsm : 0,
32863     labelxs : 0,
32864     
32865     getAutoCreate : function()
32866     {
32867         var cfg = {
32868             tag : 'div',
32869             cls : 'row roo-date-split-field-group',
32870             cn : [
32871                 {
32872                     tag : 'input',
32873                     type : 'hidden',
32874                     cls : 'form-hidden-field roo-date-split-field-group-value',
32875                     name : this.name
32876                 }
32877             ]
32878         };
32879         
32880         var labelCls = 'col-md-12';
32881         var contentCls = 'col-md-4';
32882         
32883         if(this.fieldLabel){
32884             
32885             var label = {
32886                 tag : 'div',
32887                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32888                 cn : [
32889                     {
32890                         tag : 'label',
32891                         html : this.fieldLabel
32892                     }
32893                 ]
32894             };
32895             
32896             if(this.labelAlign == 'left'){
32897             
32898                 if(this.labelWidth > 12){
32899                     label.style = "width: " + this.labelWidth + 'px';
32900                 }
32901
32902                 if(this.labelWidth < 13 && this.labelmd == 0){
32903                     this.labelmd = this.labelWidth;
32904                 }
32905
32906                 if(this.labellg > 0){
32907                     labelCls = ' col-lg-' + this.labellg;
32908                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32909                 }
32910
32911                 if(this.labelmd > 0){
32912                     labelCls = ' col-md-' + this.labelmd;
32913                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32914                 }
32915
32916                 if(this.labelsm > 0){
32917                     labelCls = ' col-sm-' + this.labelsm;
32918                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32919                 }
32920
32921                 if(this.labelxs > 0){
32922                     labelCls = ' col-xs-' + this.labelxs;
32923                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32924                 }
32925             }
32926             
32927             label.cls += ' ' + labelCls;
32928             
32929             cfg.cn.push(label);
32930         }
32931         
32932         Roo.each(['day', 'month', 'year'], function(t){
32933             cfg.cn.push({
32934                 tag : 'div',
32935                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32936             });
32937         }, this);
32938         
32939         return cfg;
32940     },
32941     
32942     inputEl: function ()
32943     {
32944         return this.el.select('.roo-date-split-field-group-value', true).first();
32945     },
32946     
32947     onRender : function(ct, position) 
32948     {
32949         var _this = this;
32950         
32951         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32952         
32953         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32954         
32955         this.dayField = new Roo.bootstrap.ComboBox({
32956             allowBlank : this.dayAllowBlank,
32957             alwaysQuery : true,
32958             displayField : 'value',
32959             editable : false,
32960             fieldLabel : '',
32961             forceSelection : true,
32962             mode : 'local',
32963             placeholder : this.dayPlaceholder,
32964             selectOnFocus : true,
32965             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32966             triggerAction : 'all',
32967             typeAhead : true,
32968             valueField : 'value',
32969             store : new Roo.data.SimpleStore({
32970                 data : (function() {    
32971                     var days = [];
32972                     _this.fireEvent('days', _this, days);
32973                     return days;
32974                 })(),
32975                 fields : [ 'value' ]
32976             }),
32977             listeners : {
32978                 select : function (_self, record, index)
32979                 {
32980                     _this.setValue(_this.getValue());
32981                 }
32982             }
32983         });
32984
32985         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32986         
32987         this.monthField = new Roo.bootstrap.MonthField({
32988             after : '<i class=\"fa fa-calendar\"></i>',
32989             allowBlank : this.monthAllowBlank,
32990             placeholder : this.monthPlaceholder,
32991             readOnly : true,
32992             listeners : {
32993                 render : function (_self)
32994                 {
32995                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32996                         e.preventDefault();
32997                         _self.focus();
32998                     });
32999                 },
33000                 select : function (_self, oldvalue, newvalue)
33001                 {
33002                     _this.setValue(_this.getValue());
33003                 }
33004             }
33005         });
33006         
33007         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33008         
33009         this.yearField = new Roo.bootstrap.ComboBox({
33010             allowBlank : this.yearAllowBlank,
33011             alwaysQuery : true,
33012             displayField : 'value',
33013             editable : false,
33014             fieldLabel : '',
33015             forceSelection : true,
33016             mode : 'local',
33017             placeholder : this.yearPlaceholder,
33018             selectOnFocus : true,
33019             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33020             triggerAction : 'all',
33021             typeAhead : true,
33022             valueField : 'value',
33023             store : new Roo.data.SimpleStore({
33024                 data : (function() {
33025                     var years = [];
33026                     _this.fireEvent('years', _this, years);
33027                     return years;
33028                 })(),
33029                 fields : [ 'value' ]
33030             }),
33031             listeners : {
33032                 select : function (_self, record, index)
33033                 {
33034                     _this.setValue(_this.getValue());
33035                 }
33036             }
33037         });
33038
33039         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33040     },
33041     
33042     setValue : function(v, format)
33043     {
33044         this.inputEl.dom.value = v;
33045         
33046         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33047         
33048         var d = Date.parseDate(v, f);
33049         
33050         if(!d){
33051             this.validate();
33052             return;
33053         }
33054         
33055         this.setDay(d.format(this.dayFormat));
33056         this.setMonth(d.format(this.monthFormat));
33057         this.setYear(d.format(this.yearFormat));
33058         
33059         this.validate();
33060         
33061         return;
33062     },
33063     
33064     setDay : function(v)
33065     {
33066         this.dayField.setValue(v);
33067         this.inputEl.dom.value = this.getValue();
33068         this.validate();
33069         return;
33070     },
33071     
33072     setMonth : function(v)
33073     {
33074         this.monthField.setValue(v, true);
33075         this.inputEl.dom.value = this.getValue();
33076         this.validate();
33077         return;
33078     },
33079     
33080     setYear : function(v)
33081     {
33082         this.yearField.setValue(v);
33083         this.inputEl.dom.value = this.getValue();
33084         this.validate();
33085         return;
33086     },
33087     
33088     getDay : function()
33089     {
33090         return this.dayField.getValue();
33091     },
33092     
33093     getMonth : function()
33094     {
33095         return this.monthField.getValue();
33096     },
33097     
33098     getYear : function()
33099     {
33100         return this.yearField.getValue();
33101     },
33102     
33103     getValue : function()
33104     {
33105         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33106         
33107         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33108         
33109         return date;
33110     },
33111     
33112     reset : function()
33113     {
33114         this.setDay('');
33115         this.setMonth('');
33116         this.setYear('');
33117         this.inputEl.dom.value = '';
33118         this.validate();
33119         return;
33120     },
33121     
33122     validate : function()
33123     {
33124         var d = this.dayField.validate();
33125         var m = this.monthField.validate();
33126         var y = this.yearField.validate();
33127         
33128         var valid = true;
33129         
33130         if(
33131                 (!this.dayAllowBlank && !d) ||
33132                 (!this.monthAllowBlank && !m) ||
33133                 (!this.yearAllowBlank && !y)
33134         ){
33135             valid = false;
33136         }
33137         
33138         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33139             return valid;
33140         }
33141         
33142         if(valid){
33143             this.markValid();
33144             return valid;
33145         }
33146         
33147         this.markInvalid();
33148         
33149         return valid;
33150     },
33151     
33152     markValid : function()
33153     {
33154         
33155         var label = this.el.select('label', true).first();
33156         var icon = this.el.select('i.fa-star', true).first();
33157
33158         if(label && icon){
33159             icon.remove();
33160         }
33161         
33162         this.fireEvent('valid', this);
33163     },
33164     
33165      /**
33166      * Mark this field as invalid
33167      * @param {String} msg The validation message
33168      */
33169     markInvalid : function(msg)
33170     {
33171         
33172         var label = this.el.select('label', true).first();
33173         var icon = this.el.select('i.fa-star', true).first();
33174
33175         if(label && !icon){
33176             this.el.select('.roo-date-split-field-label', true).createChild({
33177                 tag : 'i',
33178                 cls : 'text-danger fa fa-lg fa-star',
33179                 tooltip : 'This field is required',
33180                 style : 'margin-right:5px;'
33181             }, label, true);
33182         }
33183         
33184         this.fireEvent('invalid', this, msg);
33185     },
33186     
33187     clearInvalid : function()
33188     {
33189         var label = this.el.select('label', true).first();
33190         var icon = this.el.select('i.fa-star', true).first();
33191
33192         if(label && icon){
33193             icon.remove();
33194         }
33195         
33196         this.fireEvent('valid', this);
33197     },
33198     
33199     getName: function()
33200     {
33201         return this.name;
33202     }
33203     
33204 });
33205
33206  /**
33207  *
33208  * This is based on 
33209  * http://masonry.desandro.com
33210  *
33211  * The idea is to render all the bricks based on vertical width...
33212  *
33213  * The original code extends 'outlayer' - we might need to use that....
33214  * 
33215  */
33216
33217
33218 /**
33219  * @class Roo.bootstrap.LayoutMasonry
33220  * @extends Roo.bootstrap.Component
33221  * Bootstrap Layout Masonry class
33222  * 
33223  * @constructor
33224  * Create a new Element
33225  * @param {Object} config The config object
33226  */
33227
33228 Roo.bootstrap.LayoutMasonry = function(config){
33229     
33230     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33231     
33232     this.bricks = [];
33233     
33234     Roo.bootstrap.LayoutMasonry.register(this);
33235     
33236     this.addEvents({
33237         // raw events
33238         /**
33239          * @event layout
33240          * Fire after layout the items
33241          * @param {Roo.bootstrap.LayoutMasonry} this
33242          * @param {Roo.EventObject} e
33243          */
33244         "layout" : true
33245     });
33246     
33247 };
33248
33249 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33250     
33251     /**
33252      * @cfg {Boolean} isLayoutInstant = no animation?
33253      */   
33254     isLayoutInstant : false, // needed?
33255    
33256     /**
33257      * @cfg {Number} boxWidth  width of the columns
33258      */   
33259     boxWidth : 450,
33260     
33261       /**
33262      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33263      */   
33264     boxHeight : 0,
33265     
33266     /**
33267      * @cfg {Number} padWidth padding below box..
33268      */   
33269     padWidth : 10, 
33270     
33271     /**
33272      * @cfg {Number} gutter gutter width..
33273      */   
33274     gutter : 10,
33275     
33276      /**
33277      * @cfg {Number} maxCols maximum number of columns
33278      */   
33279     
33280     maxCols: 0,
33281     
33282     /**
33283      * @cfg {Boolean} isAutoInitial defalut true
33284      */   
33285     isAutoInitial : true, 
33286     
33287     containerWidth: 0,
33288     
33289     /**
33290      * @cfg {Boolean} isHorizontal defalut false
33291      */   
33292     isHorizontal : false, 
33293
33294     currentSize : null,
33295     
33296     tag: 'div',
33297     
33298     cls: '',
33299     
33300     bricks: null, //CompositeElement
33301     
33302     cols : 1,
33303     
33304     _isLayoutInited : false,
33305     
33306 //    isAlternative : false, // only use for vertical layout...
33307     
33308     /**
33309      * @cfg {Number} alternativePadWidth padding below box..
33310      */   
33311     alternativePadWidth : 50,
33312     
33313     selectedBrick : [],
33314     
33315     getAutoCreate : function(){
33316         
33317         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33318         
33319         var cfg = {
33320             tag: this.tag,
33321             cls: 'blog-masonary-wrapper ' + this.cls,
33322             cn : {
33323                 cls : 'mas-boxes masonary'
33324             }
33325         };
33326         
33327         return cfg;
33328     },
33329     
33330     getChildContainer: function( )
33331     {
33332         if (this.boxesEl) {
33333             return this.boxesEl;
33334         }
33335         
33336         this.boxesEl = this.el.select('.mas-boxes').first();
33337         
33338         return this.boxesEl;
33339     },
33340     
33341     
33342     initEvents : function()
33343     {
33344         var _this = this;
33345         
33346         if(this.isAutoInitial){
33347             Roo.log('hook children rendered');
33348             this.on('childrenrendered', function() {
33349                 Roo.log('children rendered');
33350                 _this.initial();
33351             } ,this);
33352         }
33353     },
33354     
33355     initial : function()
33356     {
33357         this.selectedBrick = [];
33358         
33359         this.currentSize = this.el.getBox(true);
33360         
33361         Roo.EventManager.onWindowResize(this.resize, this); 
33362
33363         if(!this.isAutoInitial){
33364             this.layout();
33365             return;
33366         }
33367         
33368         this.layout();
33369         
33370         return;
33371         //this.layout.defer(500,this);
33372         
33373     },
33374     
33375     resize : function()
33376     {
33377         var cs = this.el.getBox(true);
33378         
33379         if (
33380                 this.currentSize.width == cs.width && 
33381                 this.currentSize.x == cs.x && 
33382                 this.currentSize.height == cs.height && 
33383                 this.currentSize.y == cs.y 
33384         ) {
33385             Roo.log("no change in with or X or Y");
33386             return;
33387         }
33388         
33389         this.currentSize = cs;
33390         
33391         this.layout();
33392         
33393     },
33394     
33395     layout : function()
33396     {   
33397         this._resetLayout();
33398         
33399         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33400         
33401         this.layoutItems( isInstant );
33402       
33403         this._isLayoutInited = true;
33404         
33405         this.fireEvent('layout', this);
33406         
33407     },
33408     
33409     _resetLayout : function()
33410     {
33411         if(this.isHorizontal){
33412             this.horizontalMeasureColumns();
33413             return;
33414         }
33415         
33416         this.verticalMeasureColumns();
33417         
33418     },
33419     
33420     verticalMeasureColumns : function()
33421     {
33422         this.getContainerWidth();
33423         
33424 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33425 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33426 //            return;
33427 //        }
33428         
33429         var boxWidth = this.boxWidth + this.padWidth;
33430         
33431         if(this.containerWidth < this.boxWidth){
33432             boxWidth = this.containerWidth
33433         }
33434         
33435         var containerWidth = this.containerWidth;
33436         
33437         var cols = Math.floor(containerWidth / boxWidth);
33438         
33439         this.cols = Math.max( cols, 1 );
33440         
33441         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33442         
33443         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33444         
33445         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33446         
33447         this.colWidth = boxWidth + avail - this.padWidth;
33448         
33449         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33450         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33451     },
33452     
33453     horizontalMeasureColumns : function()
33454     {
33455         this.getContainerWidth();
33456         
33457         var boxWidth = this.boxWidth;
33458         
33459         if(this.containerWidth < boxWidth){
33460             boxWidth = this.containerWidth;
33461         }
33462         
33463         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33464         
33465         this.el.setHeight(boxWidth);
33466         
33467     },
33468     
33469     getContainerWidth : function()
33470     {
33471         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33472     },
33473     
33474     layoutItems : function( isInstant )
33475     {
33476         Roo.log(this.bricks);
33477         
33478         var items = Roo.apply([], this.bricks);
33479         
33480         if(this.isHorizontal){
33481             this._horizontalLayoutItems( items , isInstant );
33482             return;
33483         }
33484         
33485 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33486 //            this._verticalAlternativeLayoutItems( items , isInstant );
33487 //            return;
33488 //        }
33489         
33490         this._verticalLayoutItems( items , isInstant );
33491         
33492     },
33493     
33494     _verticalLayoutItems : function ( items , isInstant)
33495     {
33496         if ( !items || !items.length ) {
33497             return;
33498         }
33499         
33500         var standard = [
33501             ['xs', 'xs', 'xs', 'tall'],
33502             ['xs', 'xs', 'tall'],
33503             ['xs', 'xs', 'sm'],
33504             ['xs', 'xs', 'xs'],
33505             ['xs', 'tall'],
33506             ['xs', 'sm'],
33507             ['xs', 'xs'],
33508             ['xs'],
33509             
33510             ['sm', 'xs', 'xs'],
33511             ['sm', 'xs'],
33512             ['sm'],
33513             
33514             ['tall', 'xs', 'xs', 'xs'],
33515             ['tall', 'xs', 'xs'],
33516             ['tall', 'xs'],
33517             ['tall']
33518             
33519         ];
33520         
33521         var queue = [];
33522         
33523         var boxes = [];
33524         
33525         var box = [];
33526         
33527         Roo.each(items, function(item, k){
33528             
33529             switch (item.size) {
33530                 // these layouts take up a full box,
33531                 case 'md' :
33532                 case 'md-left' :
33533                 case 'md-right' :
33534                 case 'wide' :
33535                     
33536                     if(box.length){
33537                         boxes.push(box);
33538                         box = [];
33539                     }
33540                     
33541                     boxes.push([item]);
33542                     
33543                     break;
33544                     
33545                 case 'xs' :
33546                 case 'sm' :
33547                 case 'tall' :
33548                     
33549                     box.push(item);
33550                     
33551                     break;
33552                 default :
33553                     break;
33554                     
33555             }
33556             
33557         }, this);
33558         
33559         if(box.length){
33560             boxes.push(box);
33561             box = [];
33562         }
33563         
33564         var filterPattern = function(box, length)
33565         {
33566             if(!box.length){
33567                 return;
33568             }
33569             
33570             var match = false;
33571             
33572             var pattern = box.slice(0, length);
33573             
33574             var format = [];
33575             
33576             Roo.each(pattern, function(i){
33577                 format.push(i.size);
33578             }, this);
33579             
33580             Roo.each(standard, function(s){
33581                 
33582                 if(String(s) != String(format)){
33583                     return;
33584                 }
33585                 
33586                 match = true;
33587                 return false;
33588                 
33589             }, this);
33590             
33591             if(!match && length == 1){
33592                 return;
33593             }
33594             
33595             if(!match){
33596                 filterPattern(box, length - 1);
33597                 return;
33598             }
33599                 
33600             queue.push(pattern);
33601
33602             box = box.slice(length, box.length);
33603
33604             filterPattern(box, 4);
33605
33606             return;
33607             
33608         }
33609         
33610         Roo.each(boxes, function(box, k){
33611             
33612             if(!box.length){
33613                 return;
33614             }
33615             
33616             if(box.length == 1){
33617                 queue.push(box);
33618                 return;
33619             }
33620             
33621             filterPattern(box, 4);
33622             
33623         }, this);
33624         
33625         this._processVerticalLayoutQueue( queue, isInstant );
33626         
33627     },
33628     
33629 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33630 //    {
33631 //        if ( !items || !items.length ) {
33632 //            return;
33633 //        }
33634 //
33635 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33636 //        
33637 //    },
33638     
33639     _horizontalLayoutItems : function ( items , isInstant)
33640     {
33641         if ( !items || !items.length || items.length < 3) {
33642             return;
33643         }
33644         
33645         items.reverse();
33646         
33647         var eItems = items.slice(0, 3);
33648         
33649         items = items.slice(3, items.length);
33650         
33651         var standard = [
33652             ['xs', 'xs', 'xs', 'wide'],
33653             ['xs', 'xs', 'wide'],
33654             ['xs', 'xs', 'sm'],
33655             ['xs', 'xs', 'xs'],
33656             ['xs', 'wide'],
33657             ['xs', 'sm'],
33658             ['xs', 'xs'],
33659             ['xs'],
33660             
33661             ['sm', 'xs', 'xs'],
33662             ['sm', 'xs'],
33663             ['sm'],
33664             
33665             ['wide', 'xs', 'xs', 'xs'],
33666             ['wide', 'xs', 'xs'],
33667             ['wide', 'xs'],
33668             ['wide'],
33669             
33670             ['wide-thin']
33671         ];
33672         
33673         var queue = [];
33674         
33675         var boxes = [];
33676         
33677         var box = [];
33678         
33679         Roo.each(items, function(item, k){
33680             
33681             switch (item.size) {
33682                 case 'md' :
33683                 case 'md-left' :
33684                 case 'md-right' :
33685                 case 'tall' :
33686                     
33687                     if(box.length){
33688                         boxes.push(box);
33689                         box = [];
33690                     }
33691                     
33692                     boxes.push([item]);
33693                     
33694                     break;
33695                     
33696                 case 'xs' :
33697                 case 'sm' :
33698                 case 'wide' :
33699                 case 'wide-thin' :
33700                     
33701                     box.push(item);
33702                     
33703                     break;
33704                 default :
33705                     break;
33706                     
33707             }
33708             
33709         }, this);
33710         
33711         if(box.length){
33712             boxes.push(box);
33713             box = [];
33714         }
33715         
33716         var filterPattern = function(box, length)
33717         {
33718             if(!box.length){
33719                 return;
33720             }
33721             
33722             var match = false;
33723             
33724             var pattern = box.slice(0, length);
33725             
33726             var format = [];
33727             
33728             Roo.each(pattern, function(i){
33729                 format.push(i.size);
33730             }, this);
33731             
33732             Roo.each(standard, function(s){
33733                 
33734                 if(String(s) != String(format)){
33735                     return;
33736                 }
33737                 
33738                 match = true;
33739                 return false;
33740                 
33741             }, this);
33742             
33743             if(!match && length == 1){
33744                 return;
33745             }
33746             
33747             if(!match){
33748                 filterPattern(box, length - 1);
33749                 return;
33750             }
33751                 
33752             queue.push(pattern);
33753
33754             box = box.slice(length, box.length);
33755
33756             filterPattern(box, 4);
33757
33758             return;
33759             
33760         }
33761         
33762         Roo.each(boxes, function(box, k){
33763             
33764             if(!box.length){
33765                 return;
33766             }
33767             
33768             if(box.length == 1){
33769                 queue.push(box);
33770                 return;
33771             }
33772             
33773             filterPattern(box, 4);
33774             
33775         }, this);
33776         
33777         
33778         var prune = [];
33779         
33780         var pos = this.el.getBox(true);
33781         
33782         var minX = pos.x;
33783         
33784         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33785         
33786         var hit_end = false;
33787         
33788         Roo.each(queue, function(box){
33789             
33790             if(hit_end){
33791                 
33792                 Roo.each(box, function(b){
33793                 
33794                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33795                     b.el.hide();
33796
33797                 }, this);
33798
33799                 return;
33800             }
33801             
33802             var mx = 0;
33803             
33804             Roo.each(box, function(b){
33805                 
33806                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33807                 b.el.show();
33808
33809                 mx = Math.max(mx, b.x);
33810                 
33811             }, this);
33812             
33813             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33814             
33815             if(maxX < minX){
33816                 
33817                 Roo.each(box, function(b){
33818                 
33819                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33820                     b.el.hide();
33821                     
33822                 }, this);
33823                 
33824                 hit_end = true;
33825                 
33826                 return;
33827             }
33828             
33829             prune.push(box);
33830             
33831         }, this);
33832         
33833         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33834     },
33835     
33836     /** Sets position of item in DOM
33837     * @param {Element} item
33838     * @param {Number} x - horizontal position
33839     * @param {Number} y - vertical position
33840     * @param {Boolean} isInstant - disables transitions
33841     */
33842     _processVerticalLayoutQueue : function( queue, isInstant )
33843     {
33844         var pos = this.el.getBox(true);
33845         var x = pos.x;
33846         var y = pos.y;
33847         var maxY = [];
33848         
33849         for (var i = 0; i < this.cols; i++){
33850             maxY[i] = pos.y;
33851         }
33852         
33853         Roo.each(queue, function(box, k){
33854             
33855             var col = k % this.cols;
33856             
33857             Roo.each(box, function(b,kk){
33858                 
33859                 b.el.position('absolute');
33860                 
33861                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33862                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33863                 
33864                 if(b.size == 'md-left' || b.size == 'md-right'){
33865                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33866                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33867                 }
33868                 
33869                 b.el.setWidth(width);
33870                 b.el.setHeight(height);
33871                 // iframe?
33872                 b.el.select('iframe',true).setSize(width,height);
33873                 
33874             }, this);
33875             
33876             for (var i = 0; i < this.cols; i++){
33877                 
33878                 if(maxY[i] < maxY[col]){
33879                     col = i;
33880                     continue;
33881                 }
33882                 
33883                 col = Math.min(col, i);
33884                 
33885             }
33886             
33887             x = pos.x + col * (this.colWidth + this.padWidth);
33888             
33889             y = maxY[col];
33890             
33891             var positions = [];
33892             
33893             switch (box.length){
33894                 case 1 :
33895                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33896                     break;
33897                 case 2 :
33898                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33899                     break;
33900                 case 3 :
33901                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33902                     break;
33903                 case 4 :
33904                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33905                     break;
33906                 default :
33907                     break;
33908             }
33909             
33910             Roo.each(box, function(b,kk){
33911                 
33912                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33913                 
33914                 var sz = b.el.getSize();
33915                 
33916                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33917                 
33918             }, this);
33919             
33920         }, this);
33921         
33922         var mY = 0;
33923         
33924         for (var i = 0; i < this.cols; i++){
33925             mY = Math.max(mY, maxY[i]);
33926         }
33927         
33928         this.el.setHeight(mY - pos.y);
33929         
33930     },
33931     
33932 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33933 //    {
33934 //        var pos = this.el.getBox(true);
33935 //        var x = pos.x;
33936 //        var y = pos.y;
33937 //        var maxX = pos.right;
33938 //        
33939 //        var maxHeight = 0;
33940 //        
33941 //        Roo.each(items, function(item, k){
33942 //            
33943 //            var c = k % 2;
33944 //            
33945 //            item.el.position('absolute');
33946 //                
33947 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33948 //
33949 //            item.el.setWidth(width);
33950 //
33951 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33952 //
33953 //            item.el.setHeight(height);
33954 //            
33955 //            if(c == 0){
33956 //                item.el.setXY([x, y], isInstant ? false : true);
33957 //            } else {
33958 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33959 //            }
33960 //            
33961 //            y = y + height + this.alternativePadWidth;
33962 //            
33963 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33964 //            
33965 //        }, this);
33966 //        
33967 //        this.el.setHeight(maxHeight);
33968 //        
33969 //    },
33970     
33971     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33972     {
33973         var pos = this.el.getBox(true);
33974         
33975         var minX = pos.x;
33976         var minY = pos.y;
33977         
33978         var maxX = pos.right;
33979         
33980         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33981         
33982         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33983         
33984         Roo.each(queue, function(box, k){
33985             
33986             Roo.each(box, function(b, kk){
33987                 
33988                 b.el.position('absolute');
33989                 
33990                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33991                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33992                 
33993                 if(b.size == 'md-left' || b.size == 'md-right'){
33994                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33995                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33996                 }
33997                 
33998                 b.el.setWidth(width);
33999                 b.el.setHeight(height);
34000                 
34001             }, this);
34002             
34003             if(!box.length){
34004                 return;
34005             }
34006             
34007             var positions = [];
34008             
34009             switch (box.length){
34010                 case 1 :
34011                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34012                     break;
34013                 case 2 :
34014                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34015                     break;
34016                 case 3 :
34017                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34018                     break;
34019                 case 4 :
34020                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34021                     break;
34022                 default :
34023                     break;
34024             }
34025             
34026             Roo.each(box, function(b,kk){
34027                 
34028                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34029                 
34030                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34031                 
34032             }, this);
34033             
34034         }, this);
34035         
34036     },
34037     
34038     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34039     {
34040         Roo.each(eItems, function(b,k){
34041             
34042             b.size = (k == 0) ? 'sm' : 'xs';
34043             b.x = (k == 0) ? 2 : 1;
34044             b.y = (k == 0) ? 2 : 1;
34045             
34046             b.el.position('absolute');
34047             
34048             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34049                 
34050             b.el.setWidth(width);
34051             
34052             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34053             
34054             b.el.setHeight(height);
34055             
34056         }, this);
34057
34058         var positions = [];
34059         
34060         positions.push({
34061             x : maxX - this.unitWidth * 2 - this.gutter,
34062             y : minY
34063         });
34064         
34065         positions.push({
34066             x : maxX - this.unitWidth,
34067             y : minY + (this.unitWidth + this.gutter) * 2
34068         });
34069         
34070         positions.push({
34071             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34072             y : minY
34073         });
34074         
34075         Roo.each(eItems, function(b,k){
34076             
34077             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34078
34079         }, this);
34080         
34081     },
34082     
34083     getVerticalOneBoxColPositions : function(x, y, box)
34084     {
34085         var pos = [];
34086         
34087         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34088         
34089         if(box[0].size == 'md-left'){
34090             rand = 0;
34091         }
34092         
34093         if(box[0].size == 'md-right'){
34094             rand = 1;
34095         }
34096         
34097         pos.push({
34098             x : x + (this.unitWidth + this.gutter) * rand,
34099             y : y
34100         });
34101         
34102         return pos;
34103     },
34104     
34105     getVerticalTwoBoxColPositions : function(x, y, box)
34106     {
34107         var pos = [];
34108         
34109         if(box[0].size == 'xs'){
34110             
34111             pos.push({
34112                 x : x,
34113                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34114             });
34115
34116             pos.push({
34117                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34118                 y : y
34119             });
34120             
34121             return pos;
34122             
34123         }
34124         
34125         pos.push({
34126             x : x,
34127             y : y
34128         });
34129
34130         pos.push({
34131             x : x + (this.unitWidth + this.gutter) * 2,
34132             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34133         });
34134         
34135         return pos;
34136         
34137     },
34138     
34139     getVerticalThreeBoxColPositions : function(x, y, box)
34140     {
34141         var pos = [];
34142         
34143         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34144             
34145             pos.push({
34146                 x : x,
34147                 y : y
34148             });
34149
34150             pos.push({
34151                 x : x + (this.unitWidth + this.gutter) * 1,
34152                 y : y
34153             });
34154             
34155             pos.push({
34156                 x : x + (this.unitWidth + this.gutter) * 2,
34157                 y : y
34158             });
34159             
34160             return pos;
34161             
34162         }
34163         
34164         if(box[0].size == 'xs' && box[1].size == 'xs'){
34165             
34166             pos.push({
34167                 x : x,
34168                 y : y
34169             });
34170
34171             pos.push({
34172                 x : x,
34173                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34174             });
34175             
34176             pos.push({
34177                 x : x + (this.unitWidth + this.gutter) * 1,
34178                 y : y
34179             });
34180             
34181             return pos;
34182             
34183         }
34184         
34185         pos.push({
34186             x : x,
34187             y : y
34188         });
34189
34190         pos.push({
34191             x : x + (this.unitWidth + this.gutter) * 2,
34192             y : y
34193         });
34194
34195         pos.push({
34196             x : x + (this.unitWidth + this.gutter) * 2,
34197             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34198         });
34199             
34200         return pos;
34201         
34202     },
34203     
34204     getVerticalFourBoxColPositions : function(x, y, box)
34205     {
34206         var pos = [];
34207         
34208         if(box[0].size == 'xs'){
34209             
34210             pos.push({
34211                 x : x,
34212                 y : y
34213             });
34214
34215             pos.push({
34216                 x : x,
34217                 y : y + (this.unitHeight + this.gutter) * 1
34218             });
34219             
34220             pos.push({
34221                 x : x,
34222                 y : y + (this.unitHeight + this.gutter) * 2
34223             });
34224             
34225             pos.push({
34226                 x : x + (this.unitWidth + this.gutter) * 1,
34227                 y : y
34228             });
34229             
34230             return pos;
34231             
34232         }
34233         
34234         pos.push({
34235             x : x,
34236             y : y
34237         });
34238
34239         pos.push({
34240             x : x + (this.unitWidth + this.gutter) * 2,
34241             y : y
34242         });
34243
34244         pos.push({
34245             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34246             y : y + (this.unitHeight + this.gutter) * 1
34247         });
34248
34249         pos.push({
34250             x : x + (this.unitWidth + this.gutter) * 2,
34251             y : y + (this.unitWidth + this.gutter) * 2
34252         });
34253
34254         return pos;
34255         
34256     },
34257     
34258     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34259     {
34260         var pos = [];
34261         
34262         if(box[0].size == 'md-left'){
34263             pos.push({
34264                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34265                 y : minY
34266             });
34267             
34268             return pos;
34269         }
34270         
34271         if(box[0].size == 'md-right'){
34272             pos.push({
34273                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34274                 y : minY + (this.unitWidth + this.gutter) * 1
34275             });
34276             
34277             return pos;
34278         }
34279         
34280         var rand = Math.floor(Math.random() * (4 - box[0].y));
34281         
34282         pos.push({
34283             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34284             y : minY + (this.unitWidth + this.gutter) * rand
34285         });
34286         
34287         return pos;
34288         
34289     },
34290     
34291     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34292     {
34293         var pos = [];
34294         
34295         if(box[0].size == 'xs'){
34296             
34297             pos.push({
34298                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34299                 y : minY
34300             });
34301
34302             pos.push({
34303                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34304                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34305             });
34306             
34307             return pos;
34308             
34309         }
34310         
34311         pos.push({
34312             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34313             y : minY
34314         });
34315
34316         pos.push({
34317             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34318             y : minY + (this.unitWidth + this.gutter) * 2
34319         });
34320         
34321         return pos;
34322         
34323     },
34324     
34325     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34326     {
34327         var pos = [];
34328         
34329         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34330             
34331             pos.push({
34332                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34333                 y : minY
34334             });
34335
34336             pos.push({
34337                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34338                 y : minY + (this.unitWidth + this.gutter) * 1
34339             });
34340             
34341             pos.push({
34342                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34343                 y : minY + (this.unitWidth + this.gutter) * 2
34344             });
34345             
34346             return pos;
34347             
34348         }
34349         
34350         if(box[0].size == 'xs' && box[1].size == 'xs'){
34351             
34352             pos.push({
34353                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34354                 y : minY
34355             });
34356
34357             pos.push({
34358                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34359                 y : minY
34360             });
34361             
34362             pos.push({
34363                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34364                 y : minY + (this.unitWidth + this.gutter) * 1
34365             });
34366             
34367             return pos;
34368             
34369         }
34370         
34371         pos.push({
34372             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34373             y : minY
34374         });
34375
34376         pos.push({
34377             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34378             y : minY + (this.unitWidth + this.gutter) * 2
34379         });
34380
34381         pos.push({
34382             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34383             y : minY + (this.unitWidth + this.gutter) * 2
34384         });
34385             
34386         return pos;
34387         
34388     },
34389     
34390     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34391     {
34392         var pos = [];
34393         
34394         if(box[0].size == 'xs'){
34395             
34396             pos.push({
34397                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34398                 y : minY
34399             });
34400
34401             pos.push({
34402                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34403                 y : minY
34404             });
34405             
34406             pos.push({
34407                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34408                 y : minY
34409             });
34410             
34411             pos.push({
34412                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34413                 y : minY + (this.unitWidth + this.gutter) * 1
34414             });
34415             
34416             return pos;
34417             
34418         }
34419         
34420         pos.push({
34421             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34422             y : minY
34423         });
34424         
34425         pos.push({
34426             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34427             y : minY + (this.unitWidth + this.gutter) * 2
34428         });
34429         
34430         pos.push({
34431             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34432             y : minY + (this.unitWidth + this.gutter) * 2
34433         });
34434         
34435         pos.push({
34436             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34437             y : minY + (this.unitWidth + this.gutter) * 2
34438         });
34439
34440         return pos;
34441         
34442     },
34443     
34444     /**
34445     * remove a Masonry Brick
34446     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34447     */
34448     removeBrick : function(brick_id)
34449     {
34450         if (!brick_id) {
34451             return;
34452         }
34453         
34454         for (var i = 0; i<this.bricks.length; i++) {
34455             if (this.bricks[i].id == brick_id) {
34456                 this.bricks.splice(i,1);
34457                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34458                 this.initial();
34459             }
34460         }
34461     },
34462     
34463     /**
34464     * adds a Masonry Brick
34465     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34466     */
34467     addBrick : function(cfg)
34468     {
34469         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34470         //this.register(cn);
34471         cn.parentId = this.id;
34472         cn.render(this.el);
34473         return cn;
34474     },
34475     
34476     /**
34477     * register a Masonry Brick
34478     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34479     */
34480     
34481     register : function(brick)
34482     {
34483         this.bricks.push(brick);
34484         brick.masonryId = this.id;
34485     },
34486     
34487     /**
34488     * clear all the Masonry Brick
34489     */
34490     clearAll : function()
34491     {
34492         this.bricks = [];
34493         //this.getChildContainer().dom.innerHTML = "";
34494         this.el.dom.innerHTML = '';
34495     },
34496     
34497     getSelected : function()
34498     {
34499         if (!this.selectedBrick) {
34500             return false;
34501         }
34502         
34503         return this.selectedBrick;
34504     }
34505 });
34506
34507 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34508     
34509     groups: {},
34510      /**
34511     * register a Masonry Layout
34512     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34513     */
34514     
34515     register : function(layout)
34516     {
34517         this.groups[layout.id] = layout;
34518     },
34519     /**
34520     * fetch a  Masonry Layout based on the masonry layout ID
34521     * @param {string} the masonry layout to add
34522     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34523     */
34524     
34525     get: function(layout_id) {
34526         if (typeof(this.groups[layout_id]) == 'undefined') {
34527             return false;
34528         }
34529         return this.groups[layout_id] ;
34530     }
34531     
34532     
34533     
34534 });
34535
34536  
34537
34538  /**
34539  *
34540  * This is based on 
34541  * http://masonry.desandro.com
34542  *
34543  * The idea is to render all the bricks based on vertical width...
34544  *
34545  * The original code extends 'outlayer' - we might need to use that....
34546  * 
34547  */
34548
34549
34550 /**
34551  * @class Roo.bootstrap.LayoutMasonryAuto
34552  * @extends Roo.bootstrap.Component
34553  * Bootstrap Layout Masonry class
34554  * 
34555  * @constructor
34556  * Create a new Element
34557  * @param {Object} config The config object
34558  */
34559
34560 Roo.bootstrap.LayoutMasonryAuto = function(config){
34561     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34562 };
34563
34564 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34565     
34566       /**
34567      * @cfg {Boolean} isFitWidth  - resize the width..
34568      */   
34569     isFitWidth : false,  // options..
34570     /**
34571      * @cfg {Boolean} isOriginLeft = left align?
34572      */   
34573     isOriginLeft : true,
34574     /**
34575      * @cfg {Boolean} isOriginTop = top align?
34576      */   
34577     isOriginTop : false,
34578     /**
34579      * @cfg {Boolean} isLayoutInstant = no animation?
34580      */   
34581     isLayoutInstant : false, // needed?
34582     /**
34583      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34584      */   
34585     isResizingContainer : true,
34586     /**
34587      * @cfg {Number} columnWidth  width of the columns 
34588      */   
34589     
34590     columnWidth : 0,
34591     
34592     /**
34593      * @cfg {Number} maxCols maximum number of columns
34594      */   
34595     
34596     maxCols: 0,
34597     /**
34598      * @cfg {Number} padHeight padding below box..
34599      */   
34600     
34601     padHeight : 10, 
34602     
34603     /**
34604      * @cfg {Boolean} isAutoInitial defalut true
34605      */   
34606     
34607     isAutoInitial : true, 
34608     
34609     // private?
34610     gutter : 0,
34611     
34612     containerWidth: 0,
34613     initialColumnWidth : 0,
34614     currentSize : null,
34615     
34616     colYs : null, // array.
34617     maxY : 0,
34618     padWidth: 10,
34619     
34620     
34621     tag: 'div',
34622     cls: '',
34623     bricks: null, //CompositeElement
34624     cols : 0, // array?
34625     // element : null, // wrapped now this.el
34626     _isLayoutInited : null, 
34627     
34628     
34629     getAutoCreate : function(){
34630         
34631         var cfg = {
34632             tag: this.tag,
34633             cls: 'blog-masonary-wrapper ' + this.cls,
34634             cn : {
34635                 cls : 'mas-boxes masonary'
34636             }
34637         };
34638         
34639         return cfg;
34640     },
34641     
34642     getChildContainer: function( )
34643     {
34644         if (this.boxesEl) {
34645             return this.boxesEl;
34646         }
34647         
34648         this.boxesEl = this.el.select('.mas-boxes').first();
34649         
34650         return this.boxesEl;
34651     },
34652     
34653     
34654     initEvents : function()
34655     {
34656         var _this = this;
34657         
34658         if(this.isAutoInitial){
34659             Roo.log('hook children rendered');
34660             this.on('childrenrendered', function() {
34661                 Roo.log('children rendered');
34662                 _this.initial();
34663             } ,this);
34664         }
34665         
34666     },
34667     
34668     initial : function()
34669     {
34670         this.reloadItems();
34671
34672         this.currentSize = this.el.getBox(true);
34673
34674         /// was window resize... - let's see if this works..
34675         Roo.EventManager.onWindowResize(this.resize, this); 
34676
34677         if(!this.isAutoInitial){
34678             this.layout();
34679             return;
34680         }
34681         
34682         this.layout.defer(500,this);
34683     },
34684     
34685     reloadItems: function()
34686     {
34687         this.bricks = this.el.select('.masonry-brick', true);
34688         
34689         this.bricks.each(function(b) {
34690             //Roo.log(b.getSize());
34691             if (!b.attr('originalwidth')) {
34692                 b.attr('originalwidth',  b.getSize().width);
34693             }
34694             
34695         });
34696         
34697         Roo.log(this.bricks.elements.length);
34698     },
34699     
34700     resize : function()
34701     {
34702         Roo.log('resize');
34703         var cs = this.el.getBox(true);
34704         
34705         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34706             Roo.log("no change in with or X");
34707             return;
34708         }
34709         this.currentSize = cs;
34710         this.layout();
34711     },
34712     
34713     layout : function()
34714     {
34715          Roo.log('layout');
34716         this._resetLayout();
34717         //this._manageStamps();
34718       
34719         // don't animate first layout
34720         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34721         this.layoutItems( isInstant );
34722       
34723         // flag for initalized
34724         this._isLayoutInited = true;
34725     },
34726     
34727     layoutItems : function( isInstant )
34728     {
34729         //var items = this._getItemsForLayout( this.items );
34730         // original code supports filtering layout items.. we just ignore it..
34731         
34732         this._layoutItems( this.bricks , isInstant );
34733       
34734         this._postLayout();
34735     },
34736     _layoutItems : function ( items , isInstant)
34737     {
34738        //this.fireEvent( 'layout', this, items );
34739     
34740
34741         if ( !items || !items.elements.length ) {
34742           // no items, emit event with empty array
34743             return;
34744         }
34745
34746         var queue = [];
34747         items.each(function(item) {
34748             Roo.log("layout item");
34749             Roo.log(item);
34750             // get x/y object from method
34751             var position = this._getItemLayoutPosition( item );
34752             // enqueue
34753             position.item = item;
34754             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34755             queue.push( position );
34756         }, this);
34757       
34758         this._processLayoutQueue( queue );
34759     },
34760     /** Sets position of item in DOM
34761     * @param {Element} item
34762     * @param {Number} x - horizontal position
34763     * @param {Number} y - vertical position
34764     * @param {Boolean} isInstant - disables transitions
34765     */
34766     _processLayoutQueue : function( queue )
34767     {
34768         for ( var i=0, len = queue.length; i < len; i++ ) {
34769             var obj = queue[i];
34770             obj.item.position('absolute');
34771             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34772         }
34773     },
34774       
34775     
34776     /**
34777     * Any logic you want to do after each layout,
34778     * i.e. size the container
34779     */
34780     _postLayout : function()
34781     {
34782         this.resizeContainer();
34783     },
34784     
34785     resizeContainer : function()
34786     {
34787         if ( !this.isResizingContainer ) {
34788             return;
34789         }
34790         var size = this._getContainerSize();
34791         if ( size ) {
34792             this.el.setSize(size.width,size.height);
34793             this.boxesEl.setSize(size.width,size.height);
34794         }
34795     },
34796     
34797     
34798     
34799     _resetLayout : function()
34800     {
34801         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34802         this.colWidth = this.el.getWidth();
34803         //this.gutter = this.el.getWidth(); 
34804         
34805         this.measureColumns();
34806
34807         // reset column Y
34808         var i = this.cols;
34809         this.colYs = [];
34810         while (i--) {
34811             this.colYs.push( 0 );
34812         }
34813     
34814         this.maxY = 0;
34815     },
34816
34817     measureColumns : function()
34818     {
34819         this.getContainerWidth();
34820       // if columnWidth is 0, default to outerWidth of first item
34821         if ( !this.columnWidth ) {
34822             var firstItem = this.bricks.first();
34823             Roo.log(firstItem);
34824             this.columnWidth  = this.containerWidth;
34825             if (firstItem && firstItem.attr('originalwidth') ) {
34826                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34827             }
34828             // columnWidth fall back to item of first element
34829             Roo.log("set column width?");
34830                         this.initialColumnWidth = this.columnWidth  ;
34831
34832             // if first elem has no width, default to size of container
34833             
34834         }
34835         
34836         
34837         if (this.initialColumnWidth) {
34838             this.columnWidth = this.initialColumnWidth;
34839         }
34840         
34841         
34842             
34843         // column width is fixed at the top - however if container width get's smaller we should
34844         // reduce it...
34845         
34846         // this bit calcs how man columns..
34847             
34848         var columnWidth = this.columnWidth += this.gutter;
34849       
34850         // calculate columns
34851         var containerWidth = this.containerWidth + this.gutter;
34852         
34853         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34854         // fix rounding errors, typically with gutters
34855         var excess = columnWidth - containerWidth % columnWidth;
34856         
34857         
34858         // if overshoot is less than a pixel, round up, otherwise floor it
34859         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34860         cols = Math[ mathMethod ]( cols );
34861         this.cols = Math.max( cols, 1 );
34862         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34863         
34864          // padding positioning..
34865         var totalColWidth = this.cols * this.columnWidth;
34866         var padavail = this.containerWidth - totalColWidth;
34867         // so for 2 columns - we need 3 'pads'
34868         
34869         var padNeeded = (1+this.cols) * this.padWidth;
34870         
34871         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34872         
34873         this.columnWidth += padExtra
34874         //this.padWidth = Math.floor(padavail /  ( this.cols));
34875         
34876         // adjust colum width so that padding is fixed??
34877         
34878         // we have 3 columns ... total = width * 3
34879         // we have X left over... that should be used by 
34880         
34881         //if (this.expandC) {
34882             
34883         //}
34884         
34885         
34886         
34887     },
34888     
34889     getContainerWidth : function()
34890     {
34891        /* // container is parent if fit width
34892         var container = this.isFitWidth ? this.element.parentNode : this.element;
34893         // check that this.size and size are there
34894         // IE8 triggers resize on body size change, so they might not be
34895         
34896         var size = getSize( container );  //FIXME
34897         this.containerWidth = size && size.innerWidth; //FIXME
34898         */
34899          
34900         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34901         
34902     },
34903     
34904     _getItemLayoutPosition : function( item )  // what is item?
34905     {
34906         // we resize the item to our columnWidth..
34907       
34908         item.setWidth(this.columnWidth);
34909         item.autoBoxAdjust  = false;
34910         
34911         var sz = item.getSize();
34912  
34913         // how many columns does this brick span
34914         var remainder = this.containerWidth % this.columnWidth;
34915         
34916         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34917         // round if off by 1 pixel, otherwise use ceil
34918         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34919         colSpan = Math.min( colSpan, this.cols );
34920         
34921         // normally this should be '1' as we dont' currently allow multi width columns..
34922         
34923         var colGroup = this._getColGroup( colSpan );
34924         // get the minimum Y value from the columns
34925         var minimumY = Math.min.apply( Math, colGroup );
34926         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34927         
34928         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34929          
34930         // position the brick
34931         var position = {
34932             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34933             y: this.currentSize.y + minimumY + this.padHeight
34934         };
34935         
34936         Roo.log(position);
34937         // apply setHeight to necessary columns
34938         var setHeight = minimumY + sz.height + this.padHeight;
34939         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34940         
34941         var setSpan = this.cols + 1 - colGroup.length;
34942         for ( var i = 0; i < setSpan; i++ ) {
34943           this.colYs[ shortColIndex + i ] = setHeight ;
34944         }
34945       
34946         return position;
34947     },
34948     
34949     /**
34950      * @param {Number} colSpan - number of columns the element spans
34951      * @returns {Array} colGroup
34952      */
34953     _getColGroup : function( colSpan )
34954     {
34955         if ( colSpan < 2 ) {
34956           // if brick spans only one column, use all the column Ys
34957           return this.colYs;
34958         }
34959       
34960         var colGroup = [];
34961         // how many different places could this brick fit horizontally
34962         var groupCount = this.cols + 1 - colSpan;
34963         // for each group potential horizontal position
34964         for ( var i = 0; i < groupCount; i++ ) {
34965           // make an array of colY values for that one group
34966           var groupColYs = this.colYs.slice( i, i + colSpan );
34967           // and get the max value of the array
34968           colGroup[i] = Math.max.apply( Math, groupColYs );
34969         }
34970         return colGroup;
34971     },
34972     /*
34973     _manageStamp : function( stamp )
34974     {
34975         var stampSize =  stamp.getSize();
34976         var offset = stamp.getBox();
34977         // get the columns that this stamp affects
34978         var firstX = this.isOriginLeft ? offset.x : offset.right;
34979         var lastX = firstX + stampSize.width;
34980         var firstCol = Math.floor( firstX / this.columnWidth );
34981         firstCol = Math.max( 0, firstCol );
34982         
34983         var lastCol = Math.floor( lastX / this.columnWidth );
34984         // lastCol should not go over if multiple of columnWidth #425
34985         lastCol -= lastX % this.columnWidth ? 0 : 1;
34986         lastCol = Math.min( this.cols - 1, lastCol );
34987         
34988         // set colYs to bottom of the stamp
34989         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34990             stampSize.height;
34991             
34992         for ( var i = firstCol; i <= lastCol; i++ ) {
34993           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34994         }
34995     },
34996     */
34997     
34998     _getContainerSize : function()
34999     {
35000         this.maxY = Math.max.apply( Math, this.colYs );
35001         var size = {
35002             height: this.maxY
35003         };
35004       
35005         if ( this.isFitWidth ) {
35006             size.width = this._getContainerFitWidth();
35007         }
35008       
35009         return size;
35010     },
35011     
35012     _getContainerFitWidth : function()
35013     {
35014         var unusedCols = 0;
35015         // count unused columns
35016         var i = this.cols;
35017         while ( --i ) {
35018           if ( this.colYs[i] !== 0 ) {
35019             break;
35020           }
35021           unusedCols++;
35022         }
35023         // fit container to columns that have been used
35024         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35025     },
35026     
35027     needsResizeLayout : function()
35028     {
35029         var previousWidth = this.containerWidth;
35030         this.getContainerWidth();
35031         return previousWidth !== this.containerWidth;
35032     }
35033  
35034 });
35035
35036  
35037
35038  /*
35039  * - LGPL
35040  *
35041  * element
35042  * 
35043  */
35044
35045 /**
35046  * @class Roo.bootstrap.MasonryBrick
35047  * @extends Roo.bootstrap.Component
35048  * Bootstrap MasonryBrick class
35049  * 
35050  * @constructor
35051  * Create a new MasonryBrick
35052  * @param {Object} config The config object
35053  */
35054
35055 Roo.bootstrap.MasonryBrick = function(config){
35056     
35057     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35058     
35059     Roo.bootstrap.MasonryBrick.register(this);
35060     
35061     this.addEvents({
35062         // raw events
35063         /**
35064          * @event click
35065          * When a MasonryBrick is clcik
35066          * @param {Roo.bootstrap.MasonryBrick} this
35067          * @param {Roo.EventObject} e
35068          */
35069         "click" : true
35070     });
35071 };
35072
35073 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35074     
35075     /**
35076      * @cfg {String} title
35077      */   
35078     title : '',
35079     /**
35080      * @cfg {String} html
35081      */   
35082     html : '',
35083     /**
35084      * @cfg {String} bgimage
35085      */   
35086     bgimage : '',
35087     /**
35088      * @cfg {String} videourl
35089      */   
35090     videourl : '',
35091     /**
35092      * @cfg {String} cls
35093      */   
35094     cls : '',
35095     /**
35096      * @cfg {String} href
35097      */   
35098     href : '',
35099     /**
35100      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35101      */   
35102     size : 'xs',
35103     
35104     /**
35105      * @cfg {String} placetitle (center|bottom)
35106      */   
35107     placetitle : '',
35108     
35109     /**
35110      * @cfg {Boolean} isFitContainer defalut true
35111      */   
35112     isFitContainer : true, 
35113     
35114     /**
35115      * @cfg {Boolean} preventDefault defalut false
35116      */   
35117     preventDefault : false, 
35118     
35119     /**
35120      * @cfg {Boolean} inverse defalut false
35121      */   
35122     maskInverse : false, 
35123     
35124     getAutoCreate : function()
35125     {
35126         if(!this.isFitContainer){
35127             return this.getSplitAutoCreate();
35128         }
35129         
35130         var cls = 'masonry-brick masonry-brick-full';
35131         
35132         if(this.href.length){
35133             cls += ' masonry-brick-link';
35134         }
35135         
35136         if(this.bgimage.length){
35137             cls += ' masonry-brick-image';
35138         }
35139         
35140         if(this.maskInverse){
35141             cls += ' mask-inverse';
35142         }
35143         
35144         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35145             cls += ' enable-mask';
35146         }
35147         
35148         if(this.size){
35149             cls += ' masonry-' + this.size + '-brick';
35150         }
35151         
35152         if(this.placetitle.length){
35153             
35154             switch (this.placetitle) {
35155                 case 'center' :
35156                     cls += ' masonry-center-title';
35157                     break;
35158                 case 'bottom' :
35159                     cls += ' masonry-bottom-title';
35160                     break;
35161                 default:
35162                     break;
35163             }
35164             
35165         } else {
35166             if(!this.html.length && !this.bgimage.length){
35167                 cls += ' masonry-center-title';
35168             }
35169
35170             if(!this.html.length && this.bgimage.length){
35171                 cls += ' masonry-bottom-title';
35172             }
35173         }
35174         
35175         if(this.cls){
35176             cls += ' ' + this.cls;
35177         }
35178         
35179         var cfg = {
35180             tag: (this.href.length) ? 'a' : 'div',
35181             cls: cls,
35182             cn: [
35183                 {
35184                     tag: 'div',
35185                     cls: 'masonry-brick-mask'
35186                 },
35187                 {
35188                     tag: 'div',
35189                     cls: 'masonry-brick-paragraph',
35190                     cn: []
35191                 }
35192             ]
35193         };
35194         
35195         if(this.href.length){
35196             cfg.href = this.href;
35197         }
35198         
35199         var cn = cfg.cn[1].cn;
35200         
35201         if(this.title.length){
35202             cn.push({
35203                 tag: 'h4',
35204                 cls: 'masonry-brick-title',
35205                 html: this.title
35206             });
35207         }
35208         
35209         if(this.html.length){
35210             cn.push({
35211                 tag: 'p',
35212                 cls: 'masonry-brick-text',
35213                 html: this.html
35214             });
35215         }
35216         
35217         if (!this.title.length && !this.html.length) {
35218             cfg.cn[1].cls += ' hide';
35219         }
35220         
35221         if(this.bgimage.length){
35222             cfg.cn.push({
35223                 tag: 'img',
35224                 cls: 'masonry-brick-image-view',
35225                 src: this.bgimage
35226             });
35227         }
35228         
35229         if(this.videourl.length){
35230             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35231             // youtube support only?
35232             cfg.cn.push({
35233                 tag: 'iframe',
35234                 cls: 'masonry-brick-image-view',
35235                 src: vurl,
35236                 frameborder : 0,
35237                 allowfullscreen : true
35238             });
35239         }
35240         
35241         return cfg;
35242         
35243     },
35244     
35245     getSplitAutoCreate : function()
35246     {
35247         var cls = 'masonry-brick masonry-brick-split';
35248         
35249         if(this.href.length){
35250             cls += ' masonry-brick-link';
35251         }
35252         
35253         if(this.bgimage.length){
35254             cls += ' masonry-brick-image';
35255         }
35256         
35257         if(this.size){
35258             cls += ' masonry-' + this.size + '-brick';
35259         }
35260         
35261         switch (this.placetitle) {
35262             case 'center' :
35263                 cls += ' masonry-center-title';
35264                 break;
35265             case 'bottom' :
35266                 cls += ' masonry-bottom-title';
35267                 break;
35268             default:
35269                 if(!this.bgimage.length){
35270                     cls += ' masonry-center-title';
35271                 }
35272
35273                 if(this.bgimage.length){
35274                     cls += ' masonry-bottom-title';
35275                 }
35276                 break;
35277         }
35278         
35279         if(this.cls){
35280             cls += ' ' + this.cls;
35281         }
35282         
35283         var cfg = {
35284             tag: (this.href.length) ? 'a' : 'div',
35285             cls: cls,
35286             cn: [
35287                 {
35288                     tag: 'div',
35289                     cls: 'masonry-brick-split-head',
35290                     cn: [
35291                         {
35292                             tag: 'div',
35293                             cls: 'masonry-brick-paragraph',
35294                             cn: []
35295                         }
35296                     ]
35297                 },
35298                 {
35299                     tag: 'div',
35300                     cls: 'masonry-brick-split-body',
35301                     cn: []
35302                 }
35303             ]
35304         };
35305         
35306         if(this.href.length){
35307             cfg.href = this.href;
35308         }
35309         
35310         if(this.title.length){
35311             cfg.cn[0].cn[0].cn.push({
35312                 tag: 'h4',
35313                 cls: 'masonry-brick-title',
35314                 html: this.title
35315             });
35316         }
35317         
35318         if(this.html.length){
35319             cfg.cn[1].cn.push({
35320                 tag: 'p',
35321                 cls: 'masonry-brick-text',
35322                 html: this.html
35323             });
35324         }
35325
35326         if(this.bgimage.length){
35327             cfg.cn[0].cn.push({
35328                 tag: 'img',
35329                 cls: 'masonry-brick-image-view',
35330                 src: this.bgimage
35331             });
35332         }
35333         
35334         if(this.videourl.length){
35335             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35336             // youtube support only?
35337             cfg.cn[0].cn.cn.push({
35338                 tag: 'iframe',
35339                 cls: 'masonry-brick-image-view',
35340                 src: vurl,
35341                 frameborder : 0,
35342                 allowfullscreen : true
35343             });
35344         }
35345         
35346         return cfg;
35347     },
35348     
35349     initEvents: function() 
35350     {
35351         switch (this.size) {
35352             case 'xs' :
35353                 this.x = 1;
35354                 this.y = 1;
35355                 break;
35356             case 'sm' :
35357                 this.x = 2;
35358                 this.y = 2;
35359                 break;
35360             case 'md' :
35361             case 'md-left' :
35362             case 'md-right' :
35363                 this.x = 3;
35364                 this.y = 3;
35365                 break;
35366             case 'tall' :
35367                 this.x = 2;
35368                 this.y = 3;
35369                 break;
35370             case 'wide' :
35371                 this.x = 3;
35372                 this.y = 2;
35373                 break;
35374             case 'wide-thin' :
35375                 this.x = 3;
35376                 this.y = 1;
35377                 break;
35378                         
35379             default :
35380                 break;
35381         }
35382         
35383         if(Roo.isTouch){
35384             this.el.on('touchstart', this.onTouchStart, this);
35385             this.el.on('touchmove', this.onTouchMove, this);
35386             this.el.on('touchend', this.onTouchEnd, this);
35387             this.el.on('contextmenu', this.onContextMenu, this);
35388         } else {
35389             this.el.on('mouseenter'  ,this.enter, this);
35390             this.el.on('mouseleave', this.leave, this);
35391             this.el.on('click', this.onClick, this);
35392         }
35393         
35394         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35395             this.parent().bricks.push(this);   
35396         }
35397         
35398     },
35399     
35400     onClick: function(e, el)
35401     {
35402         var time = this.endTimer - this.startTimer;
35403         // Roo.log(e.preventDefault());
35404         if(Roo.isTouch){
35405             if(time > 1000){
35406                 e.preventDefault();
35407                 return;
35408             }
35409         }
35410         
35411         if(!this.preventDefault){
35412             return;
35413         }
35414         
35415         e.preventDefault();
35416         
35417         if (this.activeClass != '') {
35418             this.selectBrick();
35419         }
35420         
35421         this.fireEvent('click', this, e);
35422     },
35423     
35424     enter: function(e, el)
35425     {
35426         e.preventDefault();
35427         
35428         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35429             return;
35430         }
35431         
35432         if(this.bgimage.length && this.html.length){
35433             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35434         }
35435     },
35436     
35437     leave: function(e, el)
35438     {
35439         e.preventDefault();
35440         
35441         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35442             return;
35443         }
35444         
35445         if(this.bgimage.length && this.html.length){
35446             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35447         }
35448     },
35449     
35450     onTouchStart: function(e, el)
35451     {
35452 //        e.preventDefault();
35453         
35454         this.touchmoved = false;
35455         
35456         if(!this.isFitContainer){
35457             return;
35458         }
35459         
35460         if(!this.bgimage.length || !this.html.length){
35461             return;
35462         }
35463         
35464         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35465         
35466         this.timer = new Date().getTime();
35467         
35468     },
35469     
35470     onTouchMove: function(e, el)
35471     {
35472         this.touchmoved = true;
35473     },
35474     
35475     onContextMenu : function(e,el)
35476     {
35477         e.preventDefault();
35478         e.stopPropagation();
35479         return false;
35480     },
35481     
35482     onTouchEnd: function(e, el)
35483     {
35484 //        e.preventDefault();
35485         
35486         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35487         
35488             this.leave(e,el);
35489             
35490             return;
35491         }
35492         
35493         if(!this.bgimage.length || !this.html.length){
35494             
35495             if(this.href.length){
35496                 window.location.href = this.href;
35497             }
35498             
35499             return;
35500         }
35501         
35502         if(!this.isFitContainer){
35503             return;
35504         }
35505         
35506         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35507         
35508         window.location.href = this.href;
35509     },
35510     
35511     //selection on single brick only
35512     selectBrick : function() {
35513         
35514         if (!this.parentId) {
35515             return;
35516         }
35517         
35518         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35519         var index = m.selectedBrick.indexOf(this.id);
35520         
35521         if ( index > -1) {
35522             m.selectedBrick.splice(index,1);
35523             this.el.removeClass(this.activeClass);
35524             return;
35525         }
35526         
35527         for(var i = 0; i < m.selectedBrick.length; i++) {
35528             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35529             b.el.removeClass(b.activeClass);
35530         }
35531         
35532         m.selectedBrick = [];
35533         
35534         m.selectedBrick.push(this.id);
35535         this.el.addClass(this.activeClass);
35536         return;
35537     },
35538     
35539     isSelected : function(){
35540         return this.el.hasClass(this.activeClass);
35541         
35542     }
35543 });
35544
35545 Roo.apply(Roo.bootstrap.MasonryBrick, {
35546     
35547     //groups: {},
35548     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35549      /**
35550     * register a Masonry Brick
35551     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35552     */
35553     
35554     register : function(brick)
35555     {
35556         //this.groups[brick.id] = brick;
35557         this.groups.add(brick.id, brick);
35558     },
35559     /**
35560     * fetch a  masonry brick based on the masonry brick ID
35561     * @param {string} the masonry brick to add
35562     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35563     */
35564     
35565     get: function(brick_id) 
35566     {
35567         // if (typeof(this.groups[brick_id]) == 'undefined') {
35568         //     return false;
35569         // }
35570         // return this.groups[brick_id] ;
35571         
35572         if(this.groups.key(brick_id)) {
35573             return this.groups.key(brick_id);
35574         }
35575         
35576         return false;
35577     }
35578     
35579     
35580     
35581 });
35582
35583  /*
35584  * - LGPL
35585  *
35586  * element
35587  * 
35588  */
35589
35590 /**
35591  * @class Roo.bootstrap.Brick
35592  * @extends Roo.bootstrap.Component
35593  * Bootstrap Brick class
35594  * 
35595  * @constructor
35596  * Create a new Brick
35597  * @param {Object} config The config object
35598  */
35599
35600 Roo.bootstrap.Brick = function(config){
35601     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35602     
35603     this.addEvents({
35604         // raw events
35605         /**
35606          * @event click
35607          * When a Brick is click
35608          * @param {Roo.bootstrap.Brick} this
35609          * @param {Roo.EventObject} e
35610          */
35611         "click" : true
35612     });
35613 };
35614
35615 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35616     
35617     /**
35618      * @cfg {String} title
35619      */   
35620     title : '',
35621     /**
35622      * @cfg {String} html
35623      */   
35624     html : '',
35625     /**
35626      * @cfg {String} bgimage
35627      */   
35628     bgimage : '',
35629     /**
35630      * @cfg {String} cls
35631      */   
35632     cls : '',
35633     /**
35634      * @cfg {String} href
35635      */   
35636     href : '',
35637     /**
35638      * @cfg {String} video
35639      */   
35640     video : '',
35641     /**
35642      * @cfg {Boolean} square
35643      */   
35644     square : true,
35645     
35646     getAutoCreate : function()
35647     {
35648         var cls = 'roo-brick';
35649         
35650         if(this.href.length){
35651             cls += ' roo-brick-link';
35652         }
35653         
35654         if(this.bgimage.length){
35655             cls += ' roo-brick-image';
35656         }
35657         
35658         if(!this.html.length && !this.bgimage.length){
35659             cls += ' roo-brick-center-title';
35660         }
35661         
35662         if(!this.html.length && this.bgimage.length){
35663             cls += ' roo-brick-bottom-title';
35664         }
35665         
35666         if(this.cls){
35667             cls += ' ' + this.cls;
35668         }
35669         
35670         var cfg = {
35671             tag: (this.href.length) ? 'a' : 'div',
35672             cls: cls,
35673             cn: [
35674                 {
35675                     tag: 'div',
35676                     cls: 'roo-brick-paragraph',
35677                     cn: []
35678                 }
35679             ]
35680         };
35681         
35682         if(this.href.length){
35683             cfg.href = this.href;
35684         }
35685         
35686         var cn = cfg.cn[0].cn;
35687         
35688         if(this.title.length){
35689             cn.push({
35690                 tag: 'h4',
35691                 cls: 'roo-brick-title',
35692                 html: this.title
35693             });
35694         }
35695         
35696         if(this.html.length){
35697             cn.push({
35698                 tag: 'p',
35699                 cls: 'roo-brick-text',
35700                 html: this.html
35701             });
35702         } else {
35703             cn.cls += ' hide';
35704         }
35705         
35706         if(this.bgimage.length){
35707             cfg.cn.push({
35708                 tag: 'img',
35709                 cls: 'roo-brick-image-view',
35710                 src: this.bgimage
35711             });
35712         }
35713         
35714         return cfg;
35715     },
35716     
35717     initEvents: function() 
35718     {
35719         if(this.title.length || this.html.length){
35720             this.el.on('mouseenter'  ,this.enter, this);
35721             this.el.on('mouseleave', this.leave, this);
35722         }
35723         
35724         Roo.EventManager.onWindowResize(this.resize, this); 
35725         
35726         if(this.bgimage.length){
35727             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35728             this.imageEl.on('load', this.onImageLoad, this);
35729             return;
35730         }
35731         
35732         this.resize();
35733     },
35734     
35735     onImageLoad : function()
35736     {
35737         this.resize();
35738     },
35739     
35740     resize : function()
35741     {
35742         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35743         
35744         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35745         
35746         if(this.bgimage.length){
35747             var image = this.el.select('.roo-brick-image-view', true).first();
35748             
35749             image.setWidth(paragraph.getWidth());
35750             
35751             if(this.square){
35752                 image.setHeight(paragraph.getWidth());
35753             }
35754             
35755             this.el.setHeight(image.getHeight());
35756             paragraph.setHeight(image.getHeight());
35757             
35758         }
35759         
35760     },
35761     
35762     enter: function(e, el)
35763     {
35764         e.preventDefault();
35765         
35766         if(this.bgimage.length){
35767             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35768             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35769         }
35770     },
35771     
35772     leave: function(e, el)
35773     {
35774         e.preventDefault();
35775         
35776         if(this.bgimage.length){
35777             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35778             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35779         }
35780     }
35781     
35782 });
35783
35784  
35785
35786  /*
35787  * - LGPL
35788  *
35789  * Number field 
35790  */
35791
35792 /**
35793  * @class Roo.bootstrap.NumberField
35794  * @extends Roo.bootstrap.Input
35795  * Bootstrap NumberField class
35796  * 
35797  * 
35798  * 
35799  * 
35800  * @constructor
35801  * Create a new NumberField
35802  * @param {Object} config The config object
35803  */
35804
35805 Roo.bootstrap.NumberField = function(config){
35806     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35807 };
35808
35809 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35810     
35811     /**
35812      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35813      */
35814     allowDecimals : true,
35815     /**
35816      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35817      */
35818     decimalSeparator : ".",
35819     /**
35820      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35821      */
35822     decimalPrecision : 2,
35823     /**
35824      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35825      */
35826     allowNegative : true,
35827     
35828     /**
35829      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35830      */
35831     allowZero: true,
35832     /**
35833      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35834      */
35835     minValue : Number.NEGATIVE_INFINITY,
35836     /**
35837      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35838      */
35839     maxValue : Number.MAX_VALUE,
35840     /**
35841      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35842      */
35843     minText : "The minimum value for this field is {0}",
35844     /**
35845      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35846      */
35847     maxText : "The maximum value for this field is {0}",
35848     /**
35849      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35850      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35851      */
35852     nanText : "{0} is not a valid number",
35853     /**
35854      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35855      */
35856     thousandsDelimiter : false,
35857     /**
35858      * @cfg {String} valueAlign alignment of value
35859      */
35860     valueAlign : "left",
35861
35862     getAutoCreate : function()
35863     {
35864         var hiddenInput = {
35865             tag: 'input',
35866             type: 'hidden',
35867             id: Roo.id(),
35868             cls: 'hidden-number-input'
35869         };
35870         
35871         if (this.name) {
35872             hiddenInput.name = this.name;
35873         }
35874         
35875         this.name = '';
35876         
35877         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35878         
35879         this.name = hiddenInput.name;
35880         
35881         if(cfg.cn.length > 0) {
35882             cfg.cn.push(hiddenInput);
35883         }
35884         
35885         return cfg;
35886     },
35887
35888     // private
35889     initEvents : function()
35890     {   
35891         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35892         
35893         var allowed = "0123456789";
35894         
35895         if(this.allowDecimals){
35896             allowed += this.decimalSeparator;
35897         }
35898         
35899         if(this.allowNegative){
35900             allowed += "-";
35901         }
35902         
35903         if(this.thousandsDelimiter) {
35904             allowed += ",";
35905         }
35906         
35907         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35908         
35909         var keyPress = function(e){
35910             
35911             var k = e.getKey();
35912             
35913             var c = e.getCharCode();
35914             
35915             if(
35916                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35917                     allowed.indexOf(String.fromCharCode(c)) === -1
35918             ){
35919                 e.stopEvent();
35920                 return;
35921             }
35922             
35923             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35924                 return;
35925             }
35926             
35927             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35928                 e.stopEvent();
35929             }
35930         };
35931         
35932         this.el.on("keypress", keyPress, this);
35933     },
35934     
35935     validateValue : function(value)
35936     {
35937         
35938         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35939             return false;
35940         }
35941         
35942         var num = this.parseValue(value);
35943         
35944         if(isNaN(num)){
35945             this.markInvalid(String.format(this.nanText, value));
35946             return false;
35947         }
35948         
35949         if(num < this.minValue){
35950             this.markInvalid(String.format(this.minText, this.minValue));
35951             return false;
35952         }
35953         
35954         if(num > this.maxValue){
35955             this.markInvalid(String.format(this.maxText, this.maxValue));
35956             return false;
35957         }
35958         
35959         return true;
35960     },
35961
35962     getValue : function()
35963     {
35964         var v = this.hiddenEl().getValue();
35965         
35966         return this.fixPrecision(this.parseValue(v));
35967     },
35968
35969     parseValue : function(value)
35970     {
35971         if(this.thousandsDelimiter) {
35972             value += "";
35973             r = new RegExp(",", "g");
35974             value = value.replace(r, "");
35975         }
35976         
35977         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35978         return isNaN(value) ? '' : value;
35979     },
35980
35981     fixPrecision : function(value)
35982     {
35983         if(this.thousandsDelimiter) {
35984             value += "";
35985             r = new RegExp(",", "g");
35986             value = value.replace(r, "");
35987         }
35988         
35989         var nan = isNaN(value);
35990         
35991         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35992             return nan ? '' : value;
35993         }
35994         return parseFloat(value).toFixed(this.decimalPrecision);
35995     },
35996
35997     setValue : function(v)
35998     {
35999         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36000         
36001         this.value = v;
36002         
36003         if(this.rendered){
36004             
36005             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36006             
36007             this.inputEl().dom.value = (v == '') ? '' :
36008                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36009             
36010             if(!this.allowZero && v === '0') {
36011                 this.hiddenEl().dom.value = '';
36012                 this.inputEl().dom.value = '';
36013             }
36014             
36015             this.validate();
36016         }
36017     },
36018
36019     decimalPrecisionFcn : function(v)
36020     {
36021         return Math.floor(v);
36022     },
36023
36024     beforeBlur : function()
36025     {
36026         var v = this.parseValue(this.getRawValue());
36027         
36028         if(v || v === 0 || v === ''){
36029             this.setValue(v);
36030         }
36031     },
36032     
36033     hiddenEl : function()
36034     {
36035         return this.el.select('input.hidden-number-input',true).first();
36036     }
36037     
36038 });
36039
36040  
36041
36042 /*
36043 * Licence: LGPL
36044 */
36045
36046 /**
36047  * @class Roo.bootstrap.DocumentSlider
36048  * @extends Roo.bootstrap.Component
36049  * Bootstrap DocumentSlider class
36050  * 
36051  * @constructor
36052  * Create a new DocumentViewer
36053  * @param {Object} config The config object
36054  */
36055
36056 Roo.bootstrap.DocumentSlider = function(config){
36057     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36058     
36059     this.files = [];
36060     
36061     this.addEvents({
36062         /**
36063          * @event initial
36064          * Fire after initEvent
36065          * @param {Roo.bootstrap.DocumentSlider} this
36066          */
36067         "initial" : true,
36068         /**
36069          * @event update
36070          * Fire after update
36071          * @param {Roo.bootstrap.DocumentSlider} this
36072          */
36073         "update" : true,
36074         /**
36075          * @event click
36076          * Fire after click
36077          * @param {Roo.bootstrap.DocumentSlider} this
36078          */
36079         "click" : true
36080     });
36081 };
36082
36083 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36084     
36085     files : false,
36086     
36087     indicator : 0,
36088     
36089     getAutoCreate : function()
36090     {
36091         var cfg = {
36092             tag : 'div',
36093             cls : 'roo-document-slider',
36094             cn : [
36095                 {
36096                     tag : 'div',
36097                     cls : 'roo-document-slider-header',
36098                     cn : [
36099                         {
36100                             tag : 'div',
36101                             cls : 'roo-document-slider-header-title'
36102                         }
36103                     ]
36104                 },
36105                 {
36106                     tag : 'div',
36107                     cls : 'roo-document-slider-body',
36108                     cn : [
36109                         {
36110                             tag : 'div',
36111                             cls : 'roo-document-slider-prev',
36112                             cn : [
36113                                 {
36114                                     tag : 'i',
36115                                     cls : 'fa fa-chevron-left'
36116                                 }
36117                             ]
36118                         },
36119                         {
36120                             tag : 'div',
36121                             cls : 'roo-document-slider-thumb',
36122                             cn : [
36123                                 {
36124                                     tag : 'img',
36125                                     cls : 'roo-document-slider-image'
36126                                 }
36127                             ]
36128                         },
36129                         {
36130                             tag : 'div',
36131                             cls : 'roo-document-slider-next',
36132                             cn : [
36133                                 {
36134                                     tag : 'i',
36135                                     cls : 'fa fa-chevron-right'
36136                                 }
36137                             ]
36138                         }
36139                     ]
36140                 }
36141             ]
36142         };
36143         
36144         return cfg;
36145     },
36146     
36147     initEvents : function()
36148     {
36149         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36150         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36151         
36152         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36153         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36154         
36155         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36156         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36157         
36158         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36159         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36160         
36161         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36162         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36163         
36164         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36165         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36166         
36167         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36168         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36169         
36170         this.thumbEl.on('click', this.onClick, this);
36171         
36172         this.prevIndicator.on('click', this.prev, this);
36173         
36174         this.nextIndicator.on('click', this.next, this);
36175         
36176     },
36177     
36178     initial : function()
36179     {
36180         if(this.files.length){
36181             this.indicator = 1;
36182             this.update()
36183         }
36184         
36185         this.fireEvent('initial', this);
36186     },
36187     
36188     update : function()
36189     {
36190         this.imageEl.attr('src', this.files[this.indicator - 1]);
36191         
36192         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36193         
36194         this.prevIndicator.show();
36195         
36196         if(this.indicator == 1){
36197             this.prevIndicator.hide();
36198         }
36199         
36200         this.nextIndicator.show();
36201         
36202         if(this.indicator == this.files.length){
36203             this.nextIndicator.hide();
36204         }
36205         
36206         this.thumbEl.scrollTo('top');
36207         
36208         this.fireEvent('update', this);
36209     },
36210     
36211     onClick : function(e)
36212     {
36213         e.preventDefault();
36214         
36215         this.fireEvent('click', this);
36216     },
36217     
36218     prev : function(e)
36219     {
36220         e.preventDefault();
36221         
36222         this.indicator = Math.max(1, this.indicator - 1);
36223         
36224         this.update();
36225     },
36226     
36227     next : function(e)
36228     {
36229         e.preventDefault();
36230         
36231         this.indicator = Math.min(this.files.length, this.indicator + 1);
36232         
36233         this.update();
36234     }
36235 });
36236 /*
36237  * - LGPL
36238  *
36239  * RadioSet
36240  *
36241  *
36242  */
36243
36244 /**
36245  * @class Roo.bootstrap.RadioSet
36246  * @extends Roo.bootstrap.Input
36247  * Bootstrap RadioSet class
36248  * @cfg {String} indicatorpos (left|right) default left
36249  * @cfg {Boolean} inline (true|false) inline the element (default true)
36250  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36251  * @constructor
36252  * Create a new RadioSet
36253  * @param {Object} config The config object
36254  */
36255
36256 Roo.bootstrap.RadioSet = function(config){
36257     
36258     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36259     
36260     this.radioes = [];
36261     
36262     Roo.bootstrap.RadioSet.register(this);
36263     
36264     this.addEvents({
36265         /**
36266         * @event check
36267         * Fires when the element is checked or unchecked.
36268         * @param {Roo.bootstrap.RadioSet} this This radio
36269         * @param {Roo.bootstrap.Radio} item The checked item
36270         */
36271        check : true,
36272        /**
36273         * @event click
36274         * Fires when the element is click.
36275         * @param {Roo.bootstrap.RadioSet} this This radio set
36276         * @param {Roo.bootstrap.Radio} item The checked item
36277         * @param {Roo.EventObject} e The event object
36278         */
36279        click : true
36280     });
36281     
36282 };
36283
36284 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36285
36286     radioes : false,
36287     
36288     inline : true,
36289     
36290     weight : '',
36291     
36292     indicatorpos : 'left',
36293     
36294     getAutoCreate : function()
36295     {
36296         var label = {
36297             tag : 'label',
36298             cls : 'roo-radio-set-label',
36299             cn : [
36300                 {
36301                     tag : 'span',
36302                     html : this.fieldLabel
36303                 }
36304             ]
36305         };
36306         if (Roo.bootstrap.version == 3) {
36307             
36308             
36309             if(this.indicatorpos == 'left'){
36310                 label.cn.unshift({
36311                     tag : 'i',
36312                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36313                     tooltip : 'This field is required'
36314                 });
36315             } else {
36316                 label.cn.push({
36317                     tag : 'i',
36318                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36319                     tooltip : 'This field is required'
36320                 });
36321             }
36322         }
36323         var items = {
36324             tag : 'div',
36325             cls : 'roo-radio-set-items'
36326         };
36327         
36328         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36329         
36330         if (align === 'left' && this.fieldLabel.length) {
36331             
36332             items = {
36333                 cls : "roo-radio-set-right", 
36334                 cn: [
36335                     items
36336                 ]
36337             };
36338             
36339             if(this.labelWidth > 12){
36340                 label.style = "width: " + this.labelWidth + 'px';
36341             }
36342             
36343             if(this.labelWidth < 13 && this.labelmd == 0){
36344                 this.labelmd = this.labelWidth;
36345             }
36346             
36347             if(this.labellg > 0){
36348                 label.cls += ' col-lg-' + this.labellg;
36349                 items.cls += ' col-lg-' + (12 - this.labellg);
36350             }
36351             
36352             if(this.labelmd > 0){
36353                 label.cls += ' col-md-' + this.labelmd;
36354                 items.cls += ' col-md-' + (12 - this.labelmd);
36355             }
36356             
36357             if(this.labelsm > 0){
36358                 label.cls += ' col-sm-' + this.labelsm;
36359                 items.cls += ' col-sm-' + (12 - this.labelsm);
36360             }
36361             
36362             if(this.labelxs > 0){
36363                 label.cls += ' col-xs-' + this.labelxs;
36364                 items.cls += ' col-xs-' + (12 - this.labelxs);
36365             }
36366         }
36367         
36368         var cfg = {
36369             tag : 'div',
36370             cls : 'roo-radio-set',
36371             cn : [
36372                 {
36373                     tag : 'input',
36374                     cls : 'roo-radio-set-input',
36375                     type : 'hidden',
36376                     name : this.name,
36377                     value : this.value ? this.value :  ''
36378                 },
36379                 label,
36380                 items
36381             ]
36382         };
36383         
36384         if(this.weight.length){
36385             cfg.cls += ' roo-radio-' + this.weight;
36386         }
36387         
36388         if(this.inline) {
36389             cfg.cls += ' roo-radio-set-inline';
36390         }
36391         
36392         var settings=this;
36393         ['xs','sm','md','lg'].map(function(size){
36394             if (settings[size]) {
36395                 cfg.cls += ' col-' + size + '-' + settings[size];
36396             }
36397         });
36398         
36399         return cfg;
36400         
36401     },
36402
36403     initEvents : function()
36404     {
36405         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36406         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36407         
36408         if(!this.fieldLabel.length){
36409             this.labelEl.hide();
36410         }
36411         
36412         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36413         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36414         
36415         this.indicator = this.indicatorEl();
36416         
36417         if(this.indicator){
36418             this.indicator.addClass('invisible');
36419         }
36420         
36421         this.originalValue = this.getValue();
36422         
36423     },
36424     
36425     inputEl: function ()
36426     {
36427         return this.el.select('.roo-radio-set-input', true).first();
36428     },
36429     
36430     getChildContainer : function()
36431     {
36432         return this.itemsEl;
36433     },
36434     
36435     register : function(item)
36436     {
36437         this.radioes.push(item);
36438         
36439     },
36440     
36441     validate : function()
36442     {   
36443         if(this.getVisibilityEl().hasClass('hidden')){
36444             return true;
36445         }
36446         
36447         var valid = false;
36448         
36449         Roo.each(this.radioes, function(i){
36450             if(!i.checked){
36451                 return;
36452             }
36453             
36454             valid = true;
36455             return false;
36456         });
36457         
36458         if(this.allowBlank) {
36459             return true;
36460         }
36461         
36462         if(this.disabled || valid){
36463             this.markValid();
36464             return true;
36465         }
36466         
36467         this.markInvalid();
36468         return false;
36469         
36470     },
36471     
36472     markValid : function()
36473     {
36474         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36475             this.indicatorEl().removeClass('visible');
36476             this.indicatorEl().addClass('invisible');
36477         }
36478         
36479         
36480         if (Roo.bootstrap.version == 3) {
36481             this.el.removeClass([this.invalidClass, this.validClass]);
36482             this.el.addClass(this.validClass);
36483         } else {
36484             this.el.removeClass(['is-invalid','is-valid']);
36485             this.el.addClass(['is-valid']);
36486         }
36487         this.fireEvent('valid', this);
36488     },
36489     
36490     markInvalid : function(msg)
36491     {
36492         if(this.allowBlank || this.disabled){
36493             return;
36494         }
36495         
36496         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36497             this.indicatorEl().removeClass('invisible');
36498             this.indicatorEl().addClass('visible');
36499         }
36500         if (Roo.bootstrap.version == 3) {
36501             this.el.removeClass([this.invalidClass, this.validClass]);
36502             this.el.addClass(this.invalidClass);
36503         } else {
36504             this.el.removeClass(['is-invalid','is-valid']);
36505             this.el.addClass(['is-invalid']);
36506         }
36507         
36508         this.fireEvent('invalid', this, msg);
36509         
36510     },
36511     
36512     setValue : function(v, suppressEvent)
36513     {   
36514         if(this.value === v){
36515             return;
36516         }
36517         
36518         this.value = v;
36519         
36520         if(this.rendered){
36521             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36522         }
36523         
36524         Roo.each(this.radioes, function(i){
36525             i.checked = false;
36526             i.el.removeClass('checked');
36527         });
36528         
36529         Roo.each(this.radioes, function(i){
36530             
36531             if(i.value === v || i.value.toString() === v.toString()){
36532                 i.checked = true;
36533                 i.el.addClass('checked');
36534                 
36535                 if(suppressEvent !== true){
36536                     this.fireEvent('check', this, i);
36537                 }
36538                 
36539                 return false;
36540             }
36541             
36542         }, this);
36543         
36544         this.validate();
36545     },
36546     
36547     clearInvalid : function(){
36548         
36549         if(!this.el || this.preventMark){
36550             return;
36551         }
36552         
36553         this.el.removeClass([this.invalidClass]);
36554         
36555         this.fireEvent('valid', this);
36556     }
36557     
36558 });
36559
36560 Roo.apply(Roo.bootstrap.RadioSet, {
36561     
36562     groups: {},
36563     
36564     register : function(set)
36565     {
36566         this.groups[set.name] = set;
36567     },
36568     
36569     get: function(name) 
36570     {
36571         if (typeof(this.groups[name]) == 'undefined') {
36572             return false;
36573         }
36574         
36575         return this.groups[name] ;
36576     }
36577     
36578 });
36579 /*
36580  * Based on:
36581  * Ext JS Library 1.1.1
36582  * Copyright(c) 2006-2007, Ext JS, LLC.
36583  *
36584  * Originally Released Under LGPL - original licence link has changed is not relivant.
36585  *
36586  * Fork - LGPL
36587  * <script type="text/javascript">
36588  */
36589
36590
36591 /**
36592  * @class Roo.bootstrap.SplitBar
36593  * @extends Roo.util.Observable
36594  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36595  * <br><br>
36596  * Usage:
36597  * <pre><code>
36598 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36599                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36600 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36601 split.minSize = 100;
36602 split.maxSize = 600;
36603 split.animate = true;
36604 split.on('moved', splitterMoved);
36605 </code></pre>
36606  * @constructor
36607  * Create a new SplitBar
36608  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36609  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36610  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36611  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36612                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36613                         position of the SplitBar).
36614  */
36615 Roo.bootstrap.SplitBar = function(cfg){
36616     
36617     /** @private */
36618     
36619     //{
36620     //  dragElement : elm
36621     //  resizingElement: el,
36622         // optional..
36623     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36624     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36625         // existingProxy ???
36626     //}
36627     
36628     this.el = Roo.get(cfg.dragElement, true);
36629     this.el.dom.unselectable = "on";
36630     /** @private */
36631     this.resizingEl = Roo.get(cfg.resizingElement, true);
36632
36633     /**
36634      * @private
36635      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36636      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36637      * @type Number
36638      */
36639     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36640     
36641     /**
36642      * The minimum size of the resizing element. (Defaults to 0)
36643      * @type Number
36644      */
36645     this.minSize = 0;
36646     
36647     /**
36648      * The maximum size of the resizing element. (Defaults to 2000)
36649      * @type Number
36650      */
36651     this.maxSize = 2000;
36652     
36653     /**
36654      * Whether to animate the transition to the new size
36655      * @type Boolean
36656      */
36657     this.animate = false;
36658     
36659     /**
36660      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36661      * @type Boolean
36662      */
36663     this.useShim = false;
36664     
36665     /** @private */
36666     this.shim = null;
36667     
36668     if(!cfg.existingProxy){
36669         /** @private */
36670         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36671     }else{
36672         this.proxy = Roo.get(cfg.existingProxy).dom;
36673     }
36674     /** @private */
36675     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36676     
36677     /** @private */
36678     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36679     
36680     /** @private */
36681     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36682     
36683     /** @private */
36684     this.dragSpecs = {};
36685     
36686     /**
36687      * @private The adapter to use to positon and resize elements
36688      */
36689     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36690     this.adapter.init(this);
36691     
36692     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36693         /** @private */
36694         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36695         this.el.addClass("roo-splitbar-h");
36696     }else{
36697         /** @private */
36698         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36699         this.el.addClass("roo-splitbar-v");
36700     }
36701     
36702     this.addEvents({
36703         /**
36704          * @event resize
36705          * Fires when the splitter is moved (alias for {@link #event-moved})
36706          * @param {Roo.bootstrap.SplitBar} this
36707          * @param {Number} newSize the new width or height
36708          */
36709         "resize" : true,
36710         /**
36711          * @event moved
36712          * Fires when the splitter is moved
36713          * @param {Roo.bootstrap.SplitBar} this
36714          * @param {Number} newSize the new width or height
36715          */
36716         "moved" : true,
36717         /**
36718          * @event beforeresize
36719          * Fires before the splitter is dragged
36720          * @param {Roo.bootstrap.SplitBar} this
36721          */
36722         "beforeresize" : true,
36723
36724         "beforeapply" : true
36725     });
36726
36727     Roo.util.Observable.call(this);
36728 };
36729
36730 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36731     onStartProxyDrag : function(x, y){
36732         this.fireEvent("beforeresize", this);
36733         if(!this.overlay){
36734             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36735             o.unselectable();
36736             o.enableDisplayMode("block");
36737             // all splitbars share the same overlay
36738             Roo.bootstrap.SplitBar.prototype.overlay = o;
36739         }
36740         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36741         this.overlay.show();
36742         Roo.get(this.proxy).setDisplayed("block");
36743         var size = this.adapter.getElementSize(this);
36744         this.activeMinSize = this.getMinimumSize();;
36745         this.activeMaxSize = this.getMaximumSize();;
36746         var c1 = size - this.activeMinSize;
36747         var c2 = Math.max(this.activeMaxSize - size, 0);
36748         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36749             this.dd.resetConstraints();
36750             this.dd.setXConstraint(
36751                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36752                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36753             );
36754             this.dd.setYConstraint(0, 0);
36755         }else{
36756             this.dd.resetConstraints();
36757             this.dd.setXConstraint(0, 0);
36758             this.dd.setYConstraint(
36759                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36760                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36761             );
36762          }
36763         this.dragSpecs.startSize = size;
36764         this.dragSpecs.startPoint = [x, y];
36765         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36766     },
36767     
36768     /** 
36769      * @private Called after the drag operation by the DDProxy
36770      */
36771     onEndProxyDrag : function(e){
36772         Roo.get(this.proxy).setDisplayed(false);
36773         var endPoint = Roo.lib.Event.getXY(e);
36774         if(this.overlay){
36775             this.overlay.hide();
36776         }
36777         var newSize;
36778         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36779             newSize = this.dragSpecs.startSize + 
36780                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36781                     endPoint[0] - this.dragSpecs.startPoint[0] :
36782                     this.dragSpecs.startPoint[0] - endPoint[0]
36783                 );
36784         }else{
36785             newSize = this.dragSpecs.startSize + 
36786                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36787                     endPoint[1] - this.dragSpecs.startPoint[1] :
36788                     this.dragSpecs.startPoint[1] - endPoint[1]
36789                 );
36790         }
36791         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36792         if(newSize != this.dragSpecs.startSize){
36793             if(this.fireEvent('beforeapply', this, newSize) !== false){
36794                 this.adapter.setElementSize(this, newSize);
36795                 this.fireEvent("moved", this, newSize);
36796                 this.fireEvent("resize", this, newSize);
36797             }
36798         }
36799     },
36800     
36801     /**
36802      * Get the adapter this SplitBar uses
36803      * @return The adapter object
36804      */
36805     getAdapter : function(){
36806         return this.adapter;
36807     },
36808     
36809     /**
36810      * Set the adapter this SplitBar uses
36811      * @param {Object} adapter A SplitBar adapter object
36812      */
36813     setAdapter : function(adapter){
36814         this.adapter = adapter;
36815         this.adapter.init(this);
36816     },
36817     
36818     /**
36819      * Gets the minimum size for the resizing element
36820      * @return {Number} The minimum size
36821      */
36822     getMinimumSize : function(){
36823         return this.minSize;
36824     },
36825     
36826     /**
36827      * Sets the minimum size for the resizing element
36828      * @param {Number} minSize The minimum size
36829      */
36830     setMinimumSize : function(minSize){
36831         this.minSize = minSize;
36832     },
36833     
36834     /**
36835      * Gets the maximum size for the resizing element
36836      * @return {Number} The maximum size
36837      */
36838     getMaximumSize : function(){
36839         return this.maxSize;
36840     },
36841     
36842     /**
36843      * Sets the maximum size for the resizing element
36844      * @param {Number} maxSize The maximum size
36845      */
36846     setMaximumSize : function(maxSize){
36847         this.maxSize = maxSize;
36848     },
36849     
36850     /**
36851      * Sets the initialize size for the resizing element
36852      * @param {Number} size The initial size
36853      */
36854     setCurrentSize : function(size){
36855         var oldAnimate = this.animate;
36856         this.animate = false;
36857         this.adapter.setElementSize(this, size);
36858         this.animate = oldAnimate;
36859     },
36860     
36861     /**
36862      * Destroy this splitbar. 
36863      * @param {Boolean} removeEl True to remove the element
36864      */
36865     destroy : function(removeEl){
36866         if(this.shim){
36867             this.shim.remove();
36868         }
36869         this.dd.unreg();
36870         this.proxy.parentNode.removeChild(this.proxy);
36871         if(removeEl){
36872             this.el.remove();
36873         }
36874     }
36875 });
36876
36877 /**
36878  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
36879  */
36880 Roo.bootstrap.SplitBar.createProxy = function(dir){
36881     var proxy = new Roo.Element(document.createElement("div"));
36882     proxy.unselectable();
36883     var cls = 'roo-splitbar-proxy';
36884     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36885     document.body.appendChild(proxy.dom);
36886     return proxy.dom;
36887 };
36888
36889 /** 
36890  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36891  * Default Adapter. It assumes the splitter and resizing element are not positioned
36892  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36893  */
36894 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36895 };
36896
36897 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36898     // do nothing for now
36899     init : function(s){
36900     
36901     },
36902     /**
36903      * Called before drag operations to get the current size of the resizing element. 
36904      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36905      */
36906      getElementSize : function(s){
36907         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36908             return s.resizingEl.getWidth();
36909         }else{
36910             return s.resizingEl.getHeight();
36911         }
36912     },
36913     
36914     /**
36915      * Called after drag operations to set the size of the resizing element.
36916      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36917      * @param {Number} newSize The new size to set
36918      * @param {Function} onComplete A function to be invoked when resizing is complete
36919      */
36920     setElementSize : function(s, newSize, onComplete){
36921         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36922             if(!s.animate){
36923                 s.resizingEl.setWidth(newSize);
36924                 if(onComplete){
36925                     onComplete(s, newSize);
36926                 }
36927             }else{
36928                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36929             }
36930         }else{
36931             
36932             if(!s.animate){
36933                 s.resizingEl.setHeight(newSize);
36934                 if(onComplete){
36935                     onComplete(s, newSize);
36936                 }
36937             }else{
36938                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36939             }
36940         }
36941     }
36942 };
36943
36944 /** 
36945  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36946  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36947  * Adapter that  moves the splitter element to align with the resized sizing element. 
36948  * Used with an absolute positioned SplitBar.
36949  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36950  * document.body, make sure you assign an id to the body element.
36951  */
36952 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36953     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36954     this.container = Roo.get(container);
36955 };
36956
36957 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36958     init : function(s){
36959         this.basic.init(s);
36960     },
36961     
36962     getElementSize : function(s){
36963         return this.basic.getElementSize(s);
36964     },
36965     
36966     setElementSize : function(s, newSize, onComplete){
36967         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36968     },
36969     
36970     moveSplitter : function(s){
36971         var yes = Roo.bootstrap.SplitBar;
36972         switch(s.placement){
36973             case yes.LEFT:
36974                 s.el.setX(s.resizingEl.getRight());
36975                 break;
36976             case yes.RIGHT:
36977                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36978                 break;
36979             case yes.TOP:
36980                 s.el.setY(s.resizingEl.getBottom());
36981                 break;
36982             case yes.BOTTOM:
36983                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36984                 break;
36985         }
36986     }
36987 };
36988
36989 /**
36990  * Orientation constant - Create a vertical SplitBar
36991  * @static
36992  * @type Number
36993  */
36994 Roo.bootstrap.SplitBar.VERTICAL = 1;
36995
36996 /**
36997  * Orientation constant - Create a horizontal SplitBar
36998  * @static
36999  * @type Number
37000  */
37001 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37002
37003 /**
37004  * Placement constant - The resizing element is to the left of the splitter element
37005  * @static
37006  * @type Number
37007  */
37008 Roo.bootstrap.SplitBar.LEFT = 1;
37009
37010 /**
37011  * Placement constant - The resizing element is to the right of the splitter element
37012  * @static
37013  * @type Number
37014  */
37015 Roo.bootstrap.SplitBar.RIGHT = 2;
37016
37017 /**
37018  * Placement constant - The resizing element is positioned above the splitter element
37019  * @static
37020  * @type Number
37021  */
37022 Roo.bootstrap.SplitBar.TOP = 3;
37023
37024 /**
37025  * Placement constant - The resizing element is positioned under splitter element
37026  * @static
37027  * @type Number
37028  */
37029 Roo.bootstrap.SplitBar.BOTTOM = 4;
37030 Roo.namespace("Roo.bootstrap.layout");/*
37031  * Based on:
37032  * Ext JS Library 1.1.1
37033  * Copyright(c) 2006-2007, Ext JS, LLC.
37034  *
37035  * Originally Released Under LGPL - original licence link has changed is not relivant.
37036  *
37037  * Fork - LGPL
37038  * <script type="text/javascript">
37039  */
37040
37041 /**
37042  * @class Roo.bootstrap.layout.Manager
37043  * @extends Roo.bootstrap.Component
37044  * Base class for layout managers.
37045  */
37046 Roo.bootstrap.layout.Manager = function(config)
37047 {
37048     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37049
37050
37051
37052
37053
37054     /** false to disable window resize monitoring @type Boolean */
37055     this.monitorWindowResize = true;
37056     this.regions = {};
37057     this.addEvents({
37058         /**
37059          * @event layout
37060          * Fires when a layout is performed.
37061          * @param {Roo.LayoutManager} this
37062          */
37063         "layout" : true,
37064         /**
37065          * @event regionresized
37066          * Fires when the user resizes a region.
37067          * @param {Roo.LayoutRegion} region The resized region
37068          * @param {Number} newSize The new size (width for east/west, height for north/south)
37069          */
37070         "regionresized" : true,
37071         /**
37072          * @event regioncollapsed
37073          * Fires when a region is collapsed.
37074          * @param {Roo.LayoutRegion} region The collapsed region
37075          */
37076         "regioncollapsed" : true,
37077         /**
37078          * @event regionexpanded
37079          * Fires when a region is expanded.
37080          * @param {Roo.LayoutRegion} region The expanded region
37081          */
37082         "regionexpanded" : true
37083     });
37084     this.updating = false;
37085
37086     if (config.el) {
37087         this.el = Roo.get(config.el);
37088         this.initEvents();
37089     }
37090
37091 };
37092
37093 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37094
37095
37096     regions : null,
37097
37098     monitorWindowResize : true,
37099
37100
37101     updating : false,
37102
37103
37104     onRender : function(ct, position)
37105     {
37106         if(!this.el){
37107             this.el = Roo.get(ct);
37108             this.initEvents();
37109         }
37110         //this.fireEvent('render',this);
37111     },
37112
37113
37114     initEvents: function()
37115     {
37116
37117
37118         // ie scrollbar fix
37119         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37120             document.body.scroll = "no";
37121         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37122             this.el.position('relative');
37123         }
37124         this.id = this.el.id;
37125         this.el.addClass("roo-layout-container");
37126         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37127         if(this.el.dom != document.body ) {
37128             this.el.on('resize', this.layout,this);
37129             this.el.on('show', this.layout,this);
37130         }
37131
37132     },
37133
37134     /**
37135      * Returns true if this layout is currently being updated
37136      * @return {Boolean}
37137      */
37138     isUpdating : function(){
37139         return this.updating;
37140     },
37141
37142     /**
37143      * Suspend the LayoutManager from doing auto-layouts while
37144      * making multiple add or remove calls
37145      */
37146     beginUpdate : function(){
37147         this.updating = true;
37148     },
37149
37150     /**
37151      * Restore auto-layouts and optionally disable the manager from performing a layout
37152      * @param {Boolean} noLayout true to disable a layout update
37153      */
37154     endUpdate : function(noLayout){
37155         this.updating = false;
37156         if(!noLayout){
37157             this.layout();
37158         }
37159     },
37160
37161     layout: function(){
37162         // abstract...
37163     },
37164
37165     onRegionResized : function(region, newSize){
37166         this.fireEvent("regionresized", region, newSize);
37167         this.layout();
37168     },
37169
37170     onRegionCollapsed : function(region){
37171         this.fireEvent("regioncollapsed", region);
37172     },
37173
37174     onRegionExpanded : function(region){
37175         this.fireEvent("regionexpanded", region);
37176     },
37177
37178     /**
37179      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37180      * performs box-model adjustments.
37181      * @return {Object} The size as an object {width: (the width), height: (the height)}
37182      */
37183     getViewSize : function()
37184     {
37185         var size;
37186         if(this.el.dom != document.body){
37187             size = this.el.getSize();
37188         }else{
37189             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37190         }
37191         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37192         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37193         return size;
37194     },
37195
37196     /**
37197      * Returns the Element this layout is bound to.
37198      * @return {Roo.Element}
37199      */
37200     getEl : function(){
37201         return this.el;
37202     },
37203
37204     /**
37205      * Returns the specified region.
37206      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37207      * @return {Roo.LayoutRegion}
37208      */
37209     getRegion : function(target){
37210         return this.regions[target.toLowerCase()];
37211     },
37212
37213     onWindowResize : function(){
37214         if(this.monitorWindowResize){
37215             this.layout();
37216         }
37217     }
37218 });
37219 /*
37220  * Based on:
37221  * Ext JS Library 1.1.1
37222  * Copyright(c) 2006-2007, Ext JS, LLC.
37223  *
37224  * Originally Released Under LGPL - original licence link has changed is not relivant.
37225  *
37226  * Fork - LGPL
37227  * <script type="text/javascript">
37228  */
37229 /**
37230  * @class Roo.bootstrap.layout.Border
37231  * @extends Roo.bootstrap.layout.Manager
37232  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37233  * please see: examples/bootstrap/nested.html<br><br>
37234  
37235 <b>The container the layout is rendered into can be either the body element or any other element.
37236 If it is not the body element, the container needs to either be an absolute positioned element,
37237 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37238 the container size if it is not the body element.</b>
37239
37240 * @constructor
37241 * Create a new Border
37242 * @param {Object} config Configuration options
37243  */
37244 Roo.bootstrap.layout.Border = function(config){
37245     config = config || {};
37246     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37247     
37248     
37249     
37250     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37251         if(config[region]){
37252             config[region].region = region;
37253             this.addRegion(config[region]);
37254         }
37255     },this);
37256     
37257 };
37258
37259 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37260
37261 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37262     
37263     parent : false, // this might point to a 'nest' or a ???
37264     
37265     /**
37266      * Creates and adds a new region if it doesn't already exist.
37267      * @param {String} target The target region key (north, south, east, west or center).
37268      * @param {Object} config The regions config object
37269      * @return {BorderLayoutRegion} The new region
37270      */
37271     addRegion : function(config)
37272     {
37273         if(!this.regions[config.region]){
37274             var r = this.factory(config);
37275             this.bindRegion(r);
37276         }
37277         return this.regions[config.region];
37278     },
37279
37280     // private (kinda)
37281     bindRegion : function(r){
37282         this.regions[r.config.region] = r;
37283         
37284         r.on("visibilitychange",    this.layout, this);
37285         r.on("paneladded",          this.layout, this);
37286         r.on("panelremoved",        this.layout, this);
37287         r.on("invalidated",         this.layout, this);
37288         r.on("resized",             this.onRegionResized, this);
37289         r.on("collapsed",           this.onRegionCollapsed, this);
37290         r.on("expanded",            this.onRegionExpanded, this);
37291     },
37292
37293     /**
37294      * Performs a layout update.
37295      */
37296     layout : function()
37297     {
37298         if(this.updating) {
37299             return;
37300         }
37301         
37302         // render all the rebions if they have not been done alreayd?
37303         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37304             if(this.regions[region] && !this.regions[region].bodyEl){
37305                 this.regions[region].onRender(this.el)
37306             }
37307         },this);
37308         
37309         var size = this.getViewSize();
37310         var w = size.width;
37311         var h = size.height;
37312         var centerW = w;
37313         var centerH = h;
37314         var centerY = 0;
37315         var centerX = 0;
37316         //var x = 0, y = 0;
37317
37318         var rs = this.regions;
37319         var north = rs["north"];
37320         var south = rs["south"]; 
37321         var west = rs["west"];
37322         var east = rs["east"];
37323         var center = rs["center"];
37324         //if(this.hideOnLayout){ // not supported anymore
37325             //c.el.setStyle("display", "none");
37326         //}
37327         if(north && north.isVisible()){
37328             var b = north.getBox();
37329             var m = north.getMargins();
37330             b.width = w - (m.left+m.right);
37331             b.x = m.left;
37332             b.y = m.top;
37333             centerY = b.height + b.y + m.bottom;
37334             centerH -= centerY;
37335             north.updateBox(this.safeBox(b));
37336         }
37337         if(south && south.isVisible()){
37338             var b = south.getBox();
37339             var m = south.getMargins();
37340             b.width = w - (m.left+m.right);
37341             b.x = m.left;
37342             var totalHeight = (b.height + m.top + m.bottom);
37343             b.y = h - totalHeight + m.top;
37344             centerH -= totalHeight;
37345             south.updateBox(this.safeBox(b));
37346         }
37347         if(west && west.isVisible()){
37348             var b = west.getBox();
37349             var m = west.getMargins();
37350             b.height = centerH - (m.top+m.bottom);
37351             b.x = m.left;
37352             b.y = centerY + m.top;
37353             var totalWidth = (b.width + m.left + m.right);
37354             centerX += totalWidth;
37355             centerW -= totalWidth;
37356             west.updateBox(this.safeBox(b));
37357         }
37358         if(east && east.isVisible()){
37359             var b = east.getBox();
37360             var m = east.getMargins();
37361             b.height = centerH - (m.top+m.bottom);
37362             var totalWidth = (b.width + m.left + m.right);
37363             b.x = w - totalWidth + m.left;
37364             b.y = centerY + m.top;
37365             centerW -= totalWidth;
37366             east.updateBox(this.safeBox(b));
37367         }
37368         if(center){
37369             var m = center.getMargins();
37370             var centerBox = {
37371                 x: centerX + m.left,
37372                 y: centerY + m.top,
37373                 width: centerW - (m.left+m.right),
37374                 height: centerH - (m.top+m.bottom)
37375             };
37376             //if(this.hideOnLayout){
37377                 //center.el.setStyle("display", "block");
37378             //}
37379             center.updateBox(this.safeBox(centerBox));
37380         }
37381         this.el.repaint();
37382         this.fireEvent("layout", this);
37383     },
37384
37385     // private
37386     safeBox : function(box){
37387         box.width = Math.max(0, box.width);
37388         box.height = Math.max(0, box.height);
37389         return box;
37390     },
37391
37392     /**
37393      * Adds a ContentPanel (or subclass) to this layout.
37394      * @param {String} target The target region key (north, south, east, west or center).
37395      * @param {Roo.ContentPanel} panel The panel to add
37396      * @return {Roo.ContentPanel} The added panel
37397      */
37398     add : function(target, panel){
37399          
37400         target = target.toLowerCase();
37401         return this.regions[target].add(panel);
37402     },
37403
37404     /**
37405      * Remove a ContentPanel (or subclass) to this layout.
37406      * @param {String} target The target region key (north, south, east, west or center).
37407      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37408      * @return {Roo.ContentPanel} The removed panel
37409      */
37410     remove : function(target, panel){
37411         target = target.toLowerCase();
37412         return this.regions[target].remove(panel);
37413     },
37414
37415     /**
37416      * Searches all regions for a panel with the specified id
37417      * @param {String} panelId
37418      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37419      */
37420     findPanel : function(panelId){
37421         var rs = this.regions;
37422         for(var target in rs){
37423             if(typeof rs[target] != "function"){
37424                 var p = rs[target].getPanel(panelId);
37425                 if(p){
37426                     return p;
37427                 }
37428             }
37429         }
37430         return null;
37431     },
37432
37433     /**
37434      * Searches all regions for a panel with the specified id and activates (shows) it.
37435      * @param {String/ContentPanel} panelId The panels id or the panel itself
37436      * @return {Roo.ContentPanel} The shown panel or null
37437      */
37438     showPanel : function(panelId) {
37439       var rs = this.regions;
37440       for(var target in rs){
37441          var r = rs[target];
37442          if(typeof r != "function"){
37443             if(r.hasPanel(panelId)){
37444                return r.showPanel(panelId);
37445             }
37446          }
37447       }
37448       return null;
37449    },
37450
37451    /**
37452      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37453      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37454      */
37455    /*
37456     restoreState : function(provider){
37457         if(!provider){
37458             provider = Roo.state.Manager;
37459         }
37460         var sm = new Roo.LayoutStateManager();
37461         sm.init(this, provider);
37462     },
37463 */
37464  
37465  
37466     /**
37467      * Adds a xtype elements to the layout.
37468      * <pre><code>
37469
37470 layout.addxtype({
37471        xtype : 'ContentPanel',
37472        region: 'west',
37473        items: [ .... ]
37474    }
37475 );
37476
37477 layout.addxtype({
37478         xtype : 'NestedLayoutPanel',
37479         region: 'west',
37480         layout: {
37481            center: { },
37482            west: { }   
37483         },
37484         items : [ ... list of content panels or nested layout panels.. ]
37485    }
37486 );
37487 </code></pre>
37488      * @param {Object} cfg Xtype definition of item to add.
37489      */
37490     addxtype : function(cfg)
37491     {
37492         // basically accepts a pannel...
37493         // can accept a layout region..!?!?
37494         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37495         
37496         
37497         // theory?  children can only be panels??
37498         
37499         //if (!cfg.xtype.match(/Panel$/)) {
37500         //    return false;
37501         //}
37502         var ret = false;
37503         
37504         if (typeof(cfg.region) == 'undefined') {
37505             Roo.log("Failed to add Panel, region was not set");
37506             Roo.log(cfg);
37507             return false;
37508         }
37509         var region = cfg.region;
37510         delete cfg.region;
37511         
37512           
37513         var xitems = [];
37514         if (cfg.items) {
37515             xitems = cfg.items;
37516             delete cfg.items;
37517         }
37518         var nb = false;
37519         
37520         if ( region == 'center') {
37521             Roo.log("Center: " + cfg.title);
37522         }
37523         
37524         
37525         switch(cfg.xtype) 
37526         {
37527             case 'Content':  // ContentPanel (el, cfg)
37528             case 'Scroll':  // ContentPanel (el, cfg)
37529             case 'View': 
37530                 cfg.autoCreate = cfg.autoCreate || true;
37531                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37532                 //} else {
37533                 //    var el = this.el.createChild();
37534                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37535                 //}
37536                 
37537                 this.add(region, ret);
37538                 break;
37539             
37540             /*
37541             case 'TreePanel': // our new panel!
37542                 cfg.el = this.el.createChild();
37543                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37544                 this.add(region, ret);
37545                 break;
37546             */
37547             
37548             case 'Nest': 
37549                 // create a new Layout (which is  a Border Layout...
37550                 
37551                 var clayout = cfg.layout;
37552                 clayout.el  = this.el.createChild();
37553                 clayout.items   = clayout.items  || [];
37554                 
37555                 delete cfg.layout;
37556                 
37557                 // replace this exitems with the clayout ones..
37558                 xitems = clayout.items;
37559                  
37560                 // force background off if it's in center...
37561                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37562                     cfg.background = false;
37563                 }
37564                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37565                 
37566                 
37567                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37568                 //console.log('adding nested layout panel '  + cfg.toSource());
37569                 this.add(region, ret);
37570                 nb = {}; /// find first...
37571                 break;
37572             
37573             case 'Grid':
37574                 
37575                 // needs grid and region
37576                 
37577                 //var el = this.getRegion(region).el.createChild();
37578                 /*
37579                  *var el = this.el.createChild();
37580                 // create the grid first...
37581                 cfg.grid.container = el;
37582                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37583                 */
37584                 
37585                 if (region == 'center' && this.active ) {
37586                     cfg.background = false;
37587                 }
37588                 
37589                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37590                 
37591                 this.add(region, ret);
37592                 /*
37593                 if (cfg.background) {
37594                     // render grid on panel activation (if panel background)
37595                     ret.on('activate', function(gp) {
37596                         if (!gp.grid.rendered) {
37597                     //        gp.grid.render(el);
37598                         }
37599                     });
37600                 } else {
37601                   //  cfg.grid.render(el);
37602                 }
37603                 */
37604                 break;
37605            
37606            
37607             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37608                 // it was the old xcomponent building that caused this before.
37609                 // espeically if border is the top element in the tree.
37610                 ret = this;
37611                 break; 
37612                 
37613                     
37614                 
37615                 
37616                 
37617             default:
37618                 /*
37619                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37620                     
37621                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37622                     this.add(region, ret);
37623                 } else {
37624                 */
37625                     Roo.log(cfg);
37626                     throw "Can not add '" + cfg.xtype + "' to Border";
37627                     return null;
37628              
37629                                 
37630              
37631         }
37632         this.beginUpdate();
37633         // add children..
37634         var region = '';
37635         var abn = {};
37636         Roo.each(xitems, function(i)  {
37637             region = nb && i.region ? i.region : false;
37638             
37639             var add = ret.addxtype(i);
37640            
37641             if (region) {
37642                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37643                 if (!i.background) {
37644                     abn[region] = nb[region] ;
37645                 }
37646             }
37647             
37648         });
37649         this.endUpdate();
37650
37651         // make the last non-background panel active..
37652         //if (nb) { Roo.log(abn); }
37653         if (nb) {
37654             
37655             for(var r in abn) {
37656                 region = this.getRegion(r);
37657                 if (region) {
37658                     // tried using nb[r], but it does not work..
37659                      
37660                     region.showPanel(abn[r]);
37661                    
37662                 }
37663             }
37664         }
37665         return ret;
37666         
37667     },
37668     
37669     
37670 // private
37671     factory : function(cfg)
37672     {
37673         
37674         var validRegions = Roo.bootstrap.layout.Border.regions;
37675
37676         var target = cfg.region;
37677         cfg.mgr = this;
37678         
37679         var r = Roo.bootstrap.layout;
37680         Roo.log(target);
37681         switch(target){
37682             case "north":
37683                 return new r.North(cfg);
37684             case "south":
37685                 return new r.South(cfg);
37686             case "east":
37687                 return new r.East(cfg);
37688             case "west":
37689                 return new r.West(cfg);
37690             case "center":
37691                 return new r.Center(cfg);
37692         }
37693         throw 'Layout region "'+target+'" not supported.';
37694     }
37695     
37696     
37697 });
37698  /*
37699  * Based on:
37700  * Ext JS Library 1.1.1
37701  * Copyright(c) 2006-2007, Ext JS, LLC.
37702  *
37703  * Originally Released Under LGPL - original licence link has changed is not relivant.
37704  *
37705  * Fork - LGPL
37706  * <script type="text/javascript">
37707  */
37708  
37709 /**
37710  * @class Roo.bootstrap.layout.Basic
37711  * @extends Roo.util.Observable
37712  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37713  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37714  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37715  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37716  * @cfg {string}   region  the region that it inhabits..
37717  * @cfg {bool}   skipConfig skip config?
37718  * 
37719
37720  */
37721 Roo.bootstrap.layout.Basic = function(config){
37722     
37723     this.mgr = config.mgr;
37724     
37725     this.position = config.region;
37726     
37727     var skipConfig = config.skipConfig;
37728     
37729     this.events = {
37730         /**
37731          * @scope Roo.BasicLayoutRegion
37732          */
37733         
37734         /**
37735          * @event beforeremove
37736          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37737          * @param {Roo.LayoutRegion} this
37738          * @param {Roo.ContentPanel} panel The panel
37739          * @param {Object} e The cancel event object
37740          */
37741         "beforeremove" : true,
37742         /**
37743          * @event invalidated
37744          * Fires when the layout for this region is changed.
37745          * @param {Roo.LayoutRegion} this
37746          */
37747         "invalidated" : true,
37748         /**
37749          * @event visibilitychange
37750          * Fires when this region is shown or hidden 
37751          * @param {Roo.LayoutRegion} this
37752          * @param {Boolean} visibility true or false
37753          */
37754         "visibilitychange" : true,
37755         /**
37756          * @event paneladded
37757          * Fires when a panel is added. 
37758          * @param {Roo.LayoutRegion} this
37759          * @param {Roo.ContentPanel} panel The panel
37760          */
37761         "paneladded" : true,
37762         /**
37763          * @event panelremoved
37764          * Fires when a panel is removed. 
37765          * @param {Roo.LayoutRegion} this
37766          * @param {Roo.ContentPanel} panel The panel
37767          */
37768         "panelremoved" : true,
37769         /**
37770          * @event beforecollapse
37771          * Fires when this region before collapse.
37772          * @param {Roo.LayoutRegion} this
37773          */
37774         "beforecollapse" : true,
37775         /**
37776          * @event collapsed
37777          * Fires when this region is collapsed.
37778          * @param {Roo.LayoutRegion} this
37779          */
37780         "collapsed" : true,
37781         /**
37782          * @event expanded
37783          * Fires when this region is expanded.
37784          * @param {Roo.LayoutRegion} this
37785          */
37786         "expanded" : true,
37787         /**
37788          * @event slideshow
37789          * Fires when this region is slid into view.
37790          * @param {Roo.LayoutRegion} this
37791          */
37792         "slideshow" : true,
37793         /**
37794          * @event slidehide
37795          * Fires when this region slides out of view. 
37796          * @param {Roo.LayoutRegion} this
37797          */
37798         "slidehide" : true,
37799         /**
37800          * @event panelactivated
37801          * Fires when a panel is activated. 
37802          * @param {Roo.LayoutRegion} this
37803          * @param {Roo.ContentPanel} panel The activated panel
37804          */
37805         "panelactivated" : true,
37806         /**
37807          * @event resized
37808          * Fires when the user resizes this region. 
37809          * @param {Roo.LayoutRegion} this
37810          * @param {Number} newSize The new size (width for east/west, height for north/south)
37811          */
37812         "resized" : true
37813     };
37814     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37815     this.panels = new Roo.util.MixedCollection();
37816     this.panels.getKey = this.getPanelId.createDelegate(this);
37817     this.box = null;
37818     this.activePanel = null;
37819     // ensure listeners are added...
37820     
37821     if (config.listeners || config.events) {
37822         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37823             listeners : config.listeners || {},
37824             events : config.events || {}
37825         });
37826     }
37827     
37828     if(skipConfig !== true){
37829         this.applyConfig(config);
37830     }
37831 };
37832
37833 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37834 {
37835     getPanelId : function(p){
37836         return p.getId();
37837     },
37838     
37839     applyConfig : function(config){
37840         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37841         this.config = config;
37842         
37843     },
37844     
37845     /**
37846      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37847      * the width, for horizontal (north, south) the height.
37848      * @param {Number} newSize The new width or height
37849      */
37850     resizeTo : function(newSize){
37851         var el = this.el ? this.el :
37852                  (this.activePanel ? this.activePanel.getEl() : null);
37853         if(el){
37854             switch(this.position){
37855                 case "east":
37856                 case "west":
37857                     el.setWidth(newSize);
37858                     this.fireEvent("resized", this, newSize);
37859                 break;
37860                 case "north":
37861                 case "south":
37862                     el.setHeight(newSize);
37863                     this.fireEvent("resized", this, newSize);
37864                 break;                
37865             }
37866         }
37867     },
37868     
37869     getBox : function(){
37870         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37871     },
37872     
37873     getMargins : function(){
37874         return this.margins;
37875     },
37876     
37877     updateBox : function(box){
37878         this.box = box;
37879         var el = this.activePanel.getEl();
37880         el.dom.style.left = box.x + "px";
37881         el.dom.style.top = box.y + "px";
37882         this.activePanel.setSize(box.width, box.height);
37883     },
37884     
37885     /**
37886      * Returns the container element for this region.
37887      * @return {Roo.Element}
37888      */
37889     getEl : function(){
37890         return this.activePanel;
37891     },
37892     
37893     /**
37894      * Returns true if this region is currently visible.
37895      * @return {Boolean}
37896      */
37897     isVisible : function(){
37898         return this.activePanel ? true : false;
37899     },
37900     
37901     setActivePanel : function(panel){
37902         panel = this.getPanel(panel);
37903         if(this.activePanel && this.activePanel != panel){
37904             this.activePanel.setActiveState(false);
37905             this.activePanel.getEl().setLeftTop(-10000,-10000);
37906         }
37907         this.activePanel = panel;
37908         panel.setActiveState(true);
37909         if(this.box){
37910             panel.setSize(this.box.width, this.box.height);
37911         }
37912         this.fireEvent("panelactivated", this, panel);
37913         this.fireEvent("invalidated");
37914     },
37915     
37916     /**
37917      * Show the specified panel.
37918      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37919      * @return {Roo.ContentPanel} The shown panel or null
37920      */
37921     showPanel : function(panel){
37922         panel = this.getPanel(panel);
37923         if(panel){
37924             this.setActivePanel(panel);
37925         }
37926         return panel;
37927     },
37928     
37929     /**
37930      * Get the active panel for this region.
37931      * @return {Roo.ContentPanel} The active panel or null
37932      */
37933     getActivePanel : function(){
37934         return this.activePanel;
37935     },
37936     
37937     /**
37938      * Add the passed ContentPanel(s)
37939      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37940      * @return {Roo.ContentPanel} The panel added (if only one was added)
37941      */
37942     add : function(panel){
37943         if(arguments.length > 1){
37944             for(var i = 0, len = arguments.length; i < len; i++) {
37945                 this.add(arguments[i]);
37946             }
37947             return null;
37948         }
37949         if(this.hasPanel(panel)){
37950             this.showPanel(panel);
37951             return panel;
37952         }
37953         var el = panel.getEl();
37954         if(el.dom.parentNode != this.mgr.el.dom){
37955             this.mgr.el.dom.appendChild(el.dom);
37956         }
37957         if(panel.setRegion){
37958             panel.setRegion(this);
37959         }
37960         this.panels.add(panel);
37961         el.setStyle("position", "absolute");
37962         if(!panel.background){
37963             this.setActivePanel(panel);
37964             if(this.config.initialSize && this.panels.getCount()==1){
37965                 this.resizeTo(this.config.initialSize);
37966             }
37967         }
37968         this.fireEvent("paneladded", this, panel);
37969         return panel;
37970     },
37971     
37972     /**
37973      * Returns true if the panel is in this region.
37974      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37975      * @return {Boolean}
37976      */
37977     hasPanel : function(panel){
37978         if(typeof panel == "object"){ // must be panel obj
37979             panel = panel.getId();
37980         }
37981         return this.getPanel(panel) ? true : false;
37982     },
37983     
37984     /**
37985      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37986      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37987      * @param {Boolean} preservePanel Overrides the config preservePanel option
37988      * @return {Roo.ContentPanel} The panel that was removed
37989      */
37990     remove : function(panel, preservePanel){
37991         panel = this.getPanel(panel);
37992         if(!panel){
37993             return null;
37994         }
37995         var e = {};
37996         this.fireEvent("beforeremove", this, panel, e);
37997         if(e.cancel === true){
37998             return null;
37999         }
38000         var panelId = panel.getId();
38001         this.panels.removeKey(panelId);
38002         return panel;
38003     },
38004     
38005     /**
38006      * Returns the panel specified or null if it's not in this region.
38007      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38008      * @return {Roo.ContentPanel}
38009      */
38010     getPanel : function(id){
38011         if(typeof id == "object"){ // must be panel obj
38012             return id;
38013         }
38014         return this.panels.get(id);
38015     },
38016     
38017     /**
38018      * Returns this regions position (north/south/east/west/center).
38019      * @return {String} 
38020      */
38021     getPosition: function(){
38022         return this.position;    
38023     }
38024 });/*
38025  * Based on:
38026  * Ext JS Library 1.1.1
38027  * Copyright(c) 2006-2007, Ext JS, LLC.
38028  *
38029  * Originally Released Under LGPL - original licence link has changed is not relivant.
38030  *
38031  * Fork - LGPL
38032  * <script type="text/javascript">
38033  */
38034  
38035 /**
38036  * @class Roo.bootstrap.layout.Region
38037  * @extends Roo.bootstrap.layout.Basic
38038  * This class represents a region in a layout manager.
38039  
38040  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38041  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
38042  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38043  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38044  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38045  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38046  * @cfg {String}    title           The title for the region (overrides panel titles)
38047  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38048  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38049  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38050  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38051  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38052  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38053  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38054  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38055  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38056  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38057
38058  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38059  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38060  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38061  * @cfg {Number}    width           For East/West panels
38062  * @cfg {Number}    height          For North/South panels
38063  * @cfg {Boolean}   split           To show the splitter
38064  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38065  * 
38066  * @cfg {string}   cls             Extra CSS classes to add to region
38067  * 
38068  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38069  * @cfg {string}   region  the region that it inhabits..
38070  *
38071
38072  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38073  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38074
38075  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38076  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38077  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38078  */
38079 Roo.bootstrap.layout.Region = function(config)
38080 {
38081     this.applyConfig(config);
38082
38083     var mgr = config.mgr;
38084     var pos = config.region;
38085     config.skipConfig = true;
38086     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38087     
38088     if (mgr.el) {
38089         this.onRender(mgr.el);   
38090     }
38091      
38092     this.visible = true;
38093     this.collapsed = false;
38094     this.unrendered_panels = [];
38095 };
38096
38097 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38098
38099     position: '', // set by wrapper (eg. north/south etc..)
38100     unrendered_panels : null,  // unrendered panels.
38101     
38102     tabPosition : false,
38103     
38104     mgr: false, // points to 'Border'
38105     
38106     
38107     createBody : function(){
38108         /** This region's body element 
38109         * @type Roo.Element */
38110         this.bodyEl = this.el.createChild({
38111                 tag: "div",
38112                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38113         });
38114     },
38115
38116     onRender: function(ctr, pos)
38117     {
38118         var dh = Roo.DomHelper;
38119         /** This region's container element 
38120         * @type Roo.Element */
38121         this.el = dh.append(ctr.dom, {
38122                 tag: "div",
38123                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38124             }, true);
38125         /** This region's title element 
38126         * @type Roo.Element */
38127     
38128         this.titleEl = dh.append(this.el.dom,  {
38129                 tag: "div",
38130                 unselectable: "on",
38131                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38132                 children:[
38133                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38134                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38135                 ]
38136             }, true);
38137         
38138         this.titleEl.enableDisplayMode();
38139         /** This region's title text element 
38140         * @type HTMLElement */
38141         this.titleTextEl = this.titleEl.dom.firstChild;
38142         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38143         /*
38144         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38145         this.closeBtn.enableDisplayMode();
38146         this.closeBtn.on("click", this.closeClicked, this);
38147         this.closeBtn.hide();
38148     */
38149         this.createBody(this.config);
38150         if(this.config.hideWhenEmpty){
38151             this.hide();
38152             this.on("paneladded", this.validateVisibility, this);
38153             this.on("panelremoved", this.validateVisibility, this);
38154         }
38155         if(this.autoScroll){
38156             this.bodyEl.setStyle("overflow", "auto");
38157         }else{
38158             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38159         }
38160         //if(c.titlebar !== false){
38161             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38162                 this.titleEl.hide();
38163             }else{
38164                 this.titleEl.show();
38165                 if(this.config.title){
38166                     this.titleTextEl.innerHTML = this.config.title;
38167                 }
38168             }
38169         //}
38170         if(this.config.collapsed){
38171             this.collapse(true);
38172         }
38173         if(this.config.hidden){
38174             this.hide();
38175         }
38176         
38177         if (this.unrendered_panels && this.unrendered_panels.length) {
38178             for (var i =0;i< this.unrendered_panels.length; i++) {
38179                 this.add(this.unrendered_panels[i]);
38180             }
38181             this.unrendered_panels = null;
38182             
38183         }
38184         
38185     },
38186     
38187     applyConfig : function(c)
38188     {
38189         /*
38190          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38191             var dh = Roo.DomHelper;
38192             if(c.titlebar !== false){
38193                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38194                 this.collapseBtn.on("click", this.collapse, this);
38195                 this.collapseBtn.enableDisplayMode();
38196                 /*
38197                 if(c.showPin === true || this.showPin){
38198                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38199                     this.stickBtn.enableDisplayMode();
38200                     this.stickBtn.on("click", this.expand, this);
38201                     this.stickBtn.hide();
38202                 }
38203                 
38204             }
38205             */
38206             /** This region's collapsed element
38207             * @type Roo.Element */
38208             /*
38209              *
38210             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38211                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38212             ]}, true);
38213             
38214             if(c.floatable !== false){
38215                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38216                this.collapsedEl.on("click", this.collapseClick, this);
38217             }
38218
38219             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38220                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38221                    id: "message", unselectable: "on", style:{"float":"left"}});
38222                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38223              }
38224             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38225             this.expandBtn.on("click", this.expand, this);
38226             
38227         }
38228         
38229         if(this.collapseBtn){
38230             this.collapseBtn.setVisible(c.collapsible == true);
38231         }
38232         
38233         this.cmargins = c.cmargins || this.cmargins ||
38234                          (this.position == "west" || this.position == "east" ?
38235                              {top: 0, left: 2, right:2, bottom: 0} :
38236                              {top: 2, left: 0, right:0, bottom: 2});
38237         */
38238         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38239         
38240         
38241         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38242         
38243         this.autoScroll = c.autoScroll || false;
38244         
38245         
38246        
38247         
38248         this.duration = c.duration || .30;
38249         this.slideDuration = c.slideDuration || .45;
38250         this.config = c;
38251        
38252     },
38253     /**
38254      * Returns true if this region is currently visible.
38255      * @return {Boolean}
38256      */
38257     isVisible : function(){
38258         return this.visible;
38259     },
38260
38261     /**
38262      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38263      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38264      */
38265     //setCollapsedTitle : function(title){
38266     //    title = title || "&#160;";
38267      //   if(this.collapsedTitleTextEl){
38268       //      this.collapsedTitleTextEl.innerHTML = title;
38269        // }
38270     //},
38271
38272     getBox : function(){
38273         var b;
38274       //  if(!this.collapsed){
38275             b = this.el.getBox(false, true);
38276        // }else{
38277           //  b = this.collapsedEl.getBox(false, true);
38278         //}
38279         return b;
38280     },
38281
38282     getMargins : function(){
38283         return this.margins;
38284         //return this.collapsed ? this.cmargins : this.margins;
38285     },
38286 /*
38287     highlight : function(){
38288         this.el.addClass("x-layout-panel-dragover");
38289     },
38290
38291     unhighlight : function(){
38292         this.el.removeClass("x-layout-panel-dragover");
38293     },
38294 */
38295     updateBox : function(box)
38296     {
38297         if (!this.bodyEl) {
38298             return; // not rendered yet..
38299         }
38300         
38301         this.box = box;
38302         if(!this.collapsed){
38303             this.el.dom.style.left = box.x + "px";
38304             this.el.dom.style.top = box.y + "px";
38305             this.updateBody(box.width, box.height);
38306         }else{
38307             this.collapsedEl.dom.style.left = box.x + "px";
38308             this.collapsedEl.dom.style.top = box.y + "px";
38309             this.collapsedEl.setSize(box.width, box.height);
38310         }
38311         if(this.tabs){
38312             this.tabs.autoSizeTabs();
38313         }
38314     },
38315
38316     updateBody : function(w, h)
38317     {
38318         if(w !== null){
38319             this.el.setWidth(w);
38320             w -= this.el.getBorderWidth("rl");
38321             if(this.config.adjustments){
38322                 w += this.config.adjustments[0];
38323             }
38324         }
38325         if(h !== null && h > 0){
38326             this.el.setHeight(h);
38327             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38328             h -= this.el.getBorderWidth("tb");
38329             if(this.config.adjustments){
38330                 h += this.config.adjustments[1];
38331             }
38332             this.bodyEl.setHeight(h);
38333             if(this.tabs){
38334                 h = this.tabs.syncHeight(h);
38335             }
38336         }
38337         if(this.panelSize){
38338             w = w !== null ? w : this.panelSize.width;
38339             h = h !== null ? h : this.panelSize.height;
38340         }
38341         if(this.activePanel){
38342             var el = this.activePanel.getEl();
38343             w = w !== null ? w : el.getWidth();
38344             h = h !== null ? h : el.getHeight();
38345             this.panelSize = {width: w, height: h};
38346             this.activePanel.setSize(w, h);
38347         }
38348         if(Roo.isIE && this.tabs){
38349             this.tabs.el.repaint();
38350         }
38351     },
38352
38353     /**
38354      * Returns the container element for this region.
38355      * @return {Roo.Element}
38356      */
38357     getEl : function(){
38358         return this.el;
38359     },
38360
38361     /**
38362      * Hides this region.
38363      */
38364     hide : function(){
38365         //if(!this.collapsed){
38366             this.el.dom.style.left = "-2000px";
38367             this.el.hide();
38368         //}else{
38369          //   this.collapsedEl.dom.style.left = "-2000px";
38370          //   this.collapsedEl.hide();
38371        // }
38372         this.visible = false;
38373         this.fireEvent("visibilitychange", this, false);
38374     },
38375
38376     /**
38377      * Shows this region if it was previously hidden.
38378      */
38379     show : function(){
38380         //if(!this.collapsed){
38381             this.el.show();
38382         //}else{
38383         //    this.collapsedEl.show();
38384        // }
38385         this.visible = true;
38386         this.fireEvent("visibilitychange", this, true);
38387     },
38388 /*
38389     closeClicked : function(){
38390         if(this.activePanel){
38391             this.remove(this.activePanel);
38392         }
38393     },
38394
38395     collapseClick : function(e){
38396         if(this.isSlid){
38397            e.stopPropagation();
38398            this.slideIn();
38399         }else{
38400            e.stopPropagation();
38401            this.slideOut();
38402         }
38403     },
38404 */
38405     /**
38406      * Collapses this region.
38407      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38408      */
38409     /*
38410     collapse : function(skipAnim, skipCheck = false){
38411         if(this.collapsed) {
38412             return;
38413         }
38414         
38415         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38416             
38417             this.collapsed = true;
38418             if(this.split){
38419                 this.split.el.hide();
38420             }
38421             if(this.config.animate && skipAnim !== true){
38422                 this.fireEvent("invalidated", this);
38423                 this.animateCollapse();
38424             }else{
38425                 this.el.setLocation(-20000,-20000);
38426                 this.el.hide();
38427                 this.collapsedEl.show();
38428                 this.fireEvent("collapsed", this);
38429                 this.fireEvent("invalidated", this);
38430             }
38431         }
38432         
38433     },
38434 */
38435     animateCollapse : function(){
38436         // overridden
38437     },
38438
38439     /**
38440      * Expands this region if it was previously collapsed.
38441      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38442      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38443      */
38444     /*
38445     expand : function(e, skipAnim){
38446         if(e) {
38447             e.stopPropagation();
38448         }
38449         if(!this.collapsed || this.el.hasActiveFx()) {
38450             return;
38451         }
38452         if(this.isSlid){
38453             this.afterSlideIn();
38454             skipAnim = true;
38455         }
38456         this.collapsed = false;
38457         if(this.config.animate && skipAnim !== true){
38458             this.animateExpand();
38459         }else{
38460             this.el.show();
38461             if(this.split){
38462                 this.split.el.show();
38463             }
38464             this.collapsedEl.setLocation(-2000,-2000);
38465             this.collapsedEl.hide();
38466             this.fireEvent("invalidated", this);
38467             this.fireEvent("expanded", this);
38468         }
38469     },
38470 */
38471     animateExpand : function(){
38472         // overridden
38473     },
38474
38475     initTabs : function()
38476     {
38477         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38478         
38479         var ts = new Roo.bootstrap.panel.Tabs({
38480             el: this.bodyEl.dom,
38481             region : this,
38482             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38483             disableTooltips: this.config.disableTabTips,
38484             toolbar : this.config.toolbar
38485         });
38486         
38487         if(this.config.hideTabs){
38488             ts.stripWrap.setDisplayed(false);
38489         }
38490         this.tabs = ts;
38491         ts.resizeTabs = this.config.resizeTabs === true;
38492         ts.minTabWidth = this.config.minTabWidth || 40;
38493         ts.maxTabWidth = this.config.maxTabWidth || 250;
38494         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38495         ts.monitorResize = false;
38496         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38497         ts.bodyEl.addClass('roo-layout-tabs-body');
38498         this.panels.each(this.initPanelAsTab, this);
38499     },
38500
38501     initPanelAsTab : function(panel){
38502         var ti = this.tabs.addTab(
38503             panel.getEl().id,
38504             panel.getTitle(),
38505             null,
38506             this.config.closeOnTab && panel.isClosable(),
38507             panel.tpl
38508         );
38509         if(panel.tabTip !== undefined){
38510             ti.setTooltip(panel.tabTip);
38511         }
38512         ti.on("activate", function(){
38513               this.setActivePanel(panel);
38514         }, this);
38515         
38516         if(this.config.closeOnTab){
38517             ti.on("beforeclose", function(t, e){
38518                 e.cancel = true;
38519                 this.remove(panel);
38520             }, this);
38521         }
38522         
38523         panel.tabItem = ti;
38524         
38525         return ti;
38526     },
38527
38528     updatePanelTitle : function(panel, title)
38529     {
38530         if(this.activePanel == panel){
38531             this.updateTitle(title);
38532         }
38533         if(this.tabs){
38534             var ti = this.tabs.getTab(panel.getEl().id);
38535             ti.setText(title);
38536             if(panel.tabTip !== undefined){
38537                 ti.setTooltip(panel.tabTip);
38538             }
38539         }
38540     },
38541
38542     updateTitle : function(title){
38543         if(this.titleTextEl && !this.config.title){
38544             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38545         }
38546     },
38547
38548     setActivePanel : function(panel)
38549     {
38550         panel = this.getPanel(panel);
38551         if(this.activePanel && this.activePanel != panel){
38552             if(this.activePanel.setActiveState(false) === false){
38553                 return;
38554             }
38555         }
38556         this.activePanel = panel;
38557         panel.setActiveState(true);
38558         if(this.panelSize){
38559             panel.setSize(this.panelSize.width, this.panelSize.height);
38560         }
38561         if(this.closeBtn){
38562             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38563         }
38564         this.updateTitle(panel.getTitle());
38565         if(this.tabs){
38566             this.fireEvent("invalidated", this);
38567         }
38568         this.fireEvent("panelactivated", this, panel);
38569     },
38570
38571     /**
38572      * Shows the specified panel.
38573      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38574      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38575      */
38576     showPanel : function(panel)
38577     {
38578         panel = this.getPanel(panel);
38579         if(panel){
38580             if(this.tabs){
38581                 var tab = this.tabs.getTab(panel.getEl().id);
38582                 if(tab.isHidden()){
38583                     this.tabs.unhideTab(tab.id);
38584                 }
38585                 tab.activate();
38586             }else{
38587                 this.setActivePanel(panel);
38588             }
38589         }
38590         return panel;
38591     },
38592
38593     /**
38594      * Get the active panel for this region.
38595      * @return {Roo.ContentPanel} The active panel or null
38596      */
38597     getActivePanel : function(){
38598         return this.activePanel;
38599     },
38600
38601     validateVisibility : function(){
38602         if(this.panels.getCount() < 1){
38603             this.updateTitle("&#160;");
38604             this.closeBtn.hide();
38605             this.hide();
38606         }else{
38607             if(!this.isVisible()){
38608                 this.show();
38609             }
38610         }
38611     },
38612
38613     /**
38614      * Adds the passed ContentPanel(s) to this region.
38615      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38616      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38617      */
38618     add : function(panel)
38619     {
38620         if(arguments.length > 1){
38621             for(var i = 0, len = arguments.length; i < len; i++) {
38622                 this.add(arguments[i]);
38623             }
38624             return null;
38625         }
38626         
38627         // if we have not been rendered yet, then we can not really do much of this..
38628         if (!this.bodyEl) {
38629             this.unrendered_panels.push(panel);
38630             return panel;
38631         }
38632         
38633         
38634         
38635         
38636         if(this.hasPanel(panel)){
38637             this.showPanel(panel);
38638             return panel;
38639         }
38640         panel.setRegion(this);
38641         this.panels.add(panel);
38642        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38643             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38644             // and hide them... ???
38645             this.bodyEl.dom.appendChild(panel.getEl().dom);
38646             if(panel.background !== true){
38647                 this.setActivePanel(panel);
38648             }
38649             this.fireEvent("paneladded", this, panel);
38650             return panel;
38651         }
38652         */
38653         if(!this.tabs){
38654             this.initTabs();
38655         }else{
38656             this.initPanelAsTab(panel);
38657         }
38658         
38659         
38660         if(panel.background !== true){
38661             this.tabs.activate(panel.getEl().id);
38662         }
38663         this.fireEvent("paneladded", this, panel);
38664         return panel;
38665     },
38666
38667     /**
38668      * Hides the tab for the specified panel.
38669      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38670      */
38671     hidePanel : function(panel){
38672         if(this.tabs && (panel = this.getPanel(panel))){
38673             this.tabs.hideTab(panel.getEl().id);
38674         }
38675     },
38676
38677     /**
38678      * Unhides the tab for a previously hidden panel.
38679      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38680      */
38681     unhidePanel : function(panel){
38682         if(this.tabs && (panel = this.getPanel(panel))){
38683             this.tabs.unhideTab(panel.getEl().id);
38684         }
38685     },
38686
38687     clearPanels : function(){
38688         while(this.panels.getCount() > 0){
38689              this.remove(this.panels.first());
38690         }
38691     },
38692
38693     /**
38694      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38695      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38696      * @param {Boolean} preservePanel Overrides the config preservePanel option
38697      * @return {Roo.ContentPanel} The panel that was removed
38698      */
38699     remove : function(panel, preservePanel)
38700     {
38701         panel = this.getPanel(panel);
38702         if(!panel){
38703             return null;
38704         }
38705         var e = {};
38706         this.fireEvent("beforeremove", this, panel, e);
38707         if(e.cancel === true){
38708             return null;
38709         }
38710         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38711         var panelId = panel.getId();
38712         this.panels.removeKey(panelId);
38713         if(preservePanel){
38714             document.body.appendChild(panel.getEl().dom);
38715         }
38716         if(this.tabs){
38717             this.tabs.removeTab(panel.getEl().id);
38718         }else if (!preservePanel){
38719             this.bodyEl.dom.removeChild(panel.getEl().dom);
38720         }
38721         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38722             var p = this.panels.first();
38723             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38724             tempEl.appendChild(p.getEl().dom);
38725             this.bodyEl.update("");
38726             this.bodyEl.dom.appendChild(p.getEl().dom);
38727             tempEl = null;
38728             this.updateTitle(p.getTitle());
38729             this.tabs = null;
38730             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38731             this.setActivePanel(p);
38732         }
38733         panel.setRegion(null);
38734         if(this.activePanel == panel){
38735             this.activePanel = null;
38736         }
38737         if(this.config.autoDestroy !== false && preservePanel !== true){
38738             try{panel.destroy();}catch(e){}
38739         }
38740         this.fireEvent("panelremoved", this, panel);
38741         return panel;
38742     },
38743
38744     /**
38745      * Returns the TabPanel component used by this region
38746      * @return {Roo.TabPanel}
38747      */
38748     getTabs : function(){
38749         return this.tabs;
38750     },
38751
38752     createTool : function(parentEl, className){
38753         var btn = Roo.DomHelper.append(parentEl, {
38754             tag: "div",
38755             cls: "x-layout-tools-button",
38756             children: [ {
38757                 tag: "div",
38758                 cls: "roo-layout-tools-button-inner " + className,
38759                 html: "&#160;"
38760             }]
38761         }, true);
38762         btn.addClassOnOver("roo-layout-tools-button-over");
38763         return btn;
38764     }
38765 });/*
38766  * Based on:
38767  * Ext JS Library 1.1.1
38768  * Copyright(c) 2006-2007, Ext JS, LLC.
38769  *
38770  * Originally Released Under LGPL - original licence link has changed is not relivant.
38771  *
38772  * Fork - LGPL
38773  * <script type="text/javascript">
38774  */
38775  
38776
38777
38778 /**
38779  * @class Roo.SplitLayoutRegion
38780  * @extends Roo.LayoutRegion
38781  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38782  */
38783 Roo.bootstrap.layout.Split = function(config){
38784     this.cursor = config.cursor;
38785     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38786 };
38787
38788 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38789 {
38790     splitTip : "Drag to resize.",
38791     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38792     useSplitTips : false,
38793
38794     applyConfig : function(config){
38795         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38796     },
38797     
38798     onRender : function(ctr,pos) {
38799         
38800         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38801         if(!this.config.split){
38802             return;
38803         }
38804         if(!this.split){
38805             
38806             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38807                             tag: "div",
38808                             id: this.el.id + "-split",
38809                             cls: "roo-layout-split roo-layout-split-"+this.position,
38810                             html: "&#160;"
38811             });
38812             /** The SplitBar for this region 
38813             * @type Roo.SplitBar */
38814             // does not exist yet...
38815             Roo.log([this.position, this.orientation]);
38816             
38817             this.split = new Roo.bootstrap.SplitBar({
38818                 dragElement : splitEl,
38819                 resizingElement: this.el,
38820                 orientation : this.orientation
38821             });
38822             
38823             this.split.on("moved", this.onSplitMove, this);
38824             this.split.useShim = this.config.useShim === true;
38825             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38826             if(this.useSplitTips){
38827                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38828             }
38829             //if(config.collapsible){
38830             //    this.split.el.on("dblclick", this.collapse,  this);
38831             //}
38832         }
38833         if(typeof this.config.minSize != "undefined"){
38834             this.split.minSize = this.config.minSize;
38835         }
38836         if(typeof this.config.maxSize != "undefined"){
38837             this.split.maxSize = this.config.maxSize;
38838         }
38839         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38840             this.hideSplitter();
38841         }
38842         
38843     },
38844
38845     getHMaxSize : function(){
38846          var cmax = this.config.maxSize || 10000;
38847          var center = this.mgr.getRegion("center");
38848          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38849     },
38850
38851     getVMaxSize : function(){
38852          var cmax = this.config.maxSize || 10000;
38853          var center = this.mgr.getRegion("center");
38854          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38855     },
38856
38857     onSplitMove : function(split, newSize){
38858         this.fireEvent("resized", this, newSize);
38859     },
38860     
38861     /** 
38862      * Returns the {@link Roo.SplitBar} for this region.
38863      * @return {Roo.SplitBar}
38864      */
38865     getSplitBar : function(){
38866         return this.split;
38867     },
38868     
38869     hide : function(){
38870         this.hideSplitter();
38871         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38872     },
38873
38874     hideSplitter : function(){
38875         if(this.split){
38876             this.split.el.setLocation(-2000,-2000);
38877             this.split.el.hide();
38878         }
38879     },
38880
38881     show : function(){
38882         if(this.split){
38883             this.split.el.show();
38884         }
38885         Roo.bootstrap.layout.Split.superclass.show.call(this);
38886     },
38887     
38888     beforeSlide: function(){
38889         if(Roo.isGecko){// firefox overflow auto bug workaround
38890             this.bodyEl.clip();
38891             if(this.tabs) {
38892                 this.tabs.bodyEl.clip();
38893             }
38894             if(this.activePanel){
38895                 this.activePanel.getEl().clip();
38896                 
38897                 if(this.activePanel.beforeSlide){
38898                     this.activePanel.beforeSlide();
38899                 }
38900             }
38901         }
38902     },
38903     
38904     afterSlide : function(){
38905         if(Roo.isGecko){// firefox overflow auto bug workaround
38906             this.bodyEl.unclip();
38907             if(this.tabs) {
38908                 this.tabs.bodyEl.unclip();
38909             }
38910             if(this.activePanel){
38911                 this.activePanel.getEl().unclip();
38912                 if(this.activePanel.afterSlide){
38913                     this.activePanel.afterSlide();
38914                 }
38915             }
38916         }
38917     },
38918
38919     initAutoHide : function(){
38920         if(this.autoHide !== false){
38921             if(!this.autoHideHd){
38922                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38923                 this.autoHideHd = {
38924                     "mouseout": function(e){
38925                         if(!e.within(this.el, true)){
38926                             st.delay(500);
38927                         }
38928                     },
38929                     "mouseover" : function(e){
38930                         st.cancel();
38931                     },
38932                     scope : this
38933                 };
38934             }
38935             this.el.on(this.autoHideHd);
38936         }
38937     },
38938
38939     clearAutoHide : function(){
38940         if(this.autoHide !== false){
38941             this.el.un("mouseout", this.autoHideHd.mouseout);
38942             this.el.un("mouseover", this.autoHideHd.mouseover);
38943         }
38944     },
38945
38946     clearMonitor : function(){
38947         Roo.get(document).un("click", this.slideInIf, this);
38948     },
38949
38950     // these names are backwards but not changed for compat
38951     slideOut : function(){
38952         if(this.isSlid || this.el.hasActiveFx()){
38953             return;
38954         }
38955         this.isSlid = true;
38956         if(this.collapseBtn){
38957             this.collapseBtn.hide();
38958         }
38959         this.closeBtnState = this.closeBtn.getStyle('display');
38960         this.closeBtn.hide();
38961         if(this.stickBtn){
38962             this.stickBtn.show();
38963         }
38964         this.el.show();
38965         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38966         this.beforeSlide();
38967         this.el.setStyle("z-index", 10001);
38968         this.el.slideIn(this.getSlideAnchor(), {
38969             callback: function(){
38970                 this.afterSlide();
38971                 this.initAutoHide();
38972                 Roo.get(document).on("click", this.slideInIf, this);
38973                 this.fireEvent("slideshow", this);
38974             },
38975             scope: this,
38976             block: true
38977         });
38978     },
38979
38980     afterSlideIn : function(){
38981         this.clearAutoHide();
38982         this.isSlid = false;
38983         this.clearMonitor();
38984         this.el.setStyle("z-index", "");
38985         if(this.collapseBtn){
38986             this.collapseBtn.show();
38987         }
38988         this.closeBtn.setStyle('display', this.closeBtnState);
38989         if(this.stickBtn){
38990             this.stickBtn.hide();
38991         }
38992         this.fireEvent("slidehide", this);
38993     },
38994
38995     slideIn : function(cb){
38996         if(!this.isSlid || this.el.hasActiveFx()){
38997             Roo.callback(cb);
38998             return;
38999         }
39000         this.isSlid = false;
39001         this.beforeSlide();
39002         this.el.slideOut(this.getSlideAnchor(), {
39003             callback: function(){
39004                 this.el.setLeftTop(-10000, -10000);
39005                 this.afterSlide();
39006                 this.afterSlideIn();
39007                 Roo.callback(cb);
39008             },
39009             scope: this,
39010             block: true
39011         });
39012     },
39013     
39014     slideInIf : function(e){
39015         if(!e.within(this.el)){
39016             this.slideIn();
39017         }
39018     },
39019
39020     animateCollapse : function(){
39021         this.beforeSlide();
39022         this.el.setStyle("z-index", 20000);
39023         var anchor = this.getSlideAnchor();
39024         this.el.slideOut(anchor, {
39025             callback : function(){
39026                 this.el.setStyle("z-index", "");
39027                 this.collapsedEl.slideIn(anchor, {duration:.3});
39028                 this.afterSlide();
39029                 this.el.setLocation(-10000,-10000);
39030                 this.el.hide();
39031                 this.fireEvent("collapsed", this);
39032             },
39033             scope: this,
39034             block: true
39035         });
39036     },
39037
39038     animateExpand : function(){
39039         this.beforeSlide();
39040         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39041         this.el.setStyle("z-index", 20000);
39042         this.collapsedEl.hide({
39043             duration:.1
39044         });
39045         this.el.slideIn(this.getSlideAnchor(), {
39046             callback : function(){
39047                 this.el.setStyle("z-index", "");
39048                 this.afterSlide();
39049                 if(this.split){
39050                     this.split.el.show();
39051                 }
39052                 this.fireEvent("invalidated", this);
39053                 this.fireEvent("expanded", this);
39054             },
39055             scope: this,
39056             block: true
39057         });
39058     },
39059
39060     anchors : {
39061         "west" : "left",
39062         "east" : "right",
39063         "north" : "top",
39064         "south" : "bottom"
39065     },
39066
39067     sanchors : {
39068         "west" : "l",
39069         "east" : "r",
39070         "north" : "t",
39071         "south" : "b"
39072     },
39073
39074     canchors : {
39075         "west" : "tl-tr",
39076         "east" : "tr-tl",
39077         "north" : "tl-bl",
39078         "south" : "bl-tl"
39079     },
39080
39081     getAnchor : function(){
39082         return this.anchors[this.position];
39083     },
39084
39085     getCollapseAnchor : function(){
39086         return this.canchors[this.position];
39087     },
39088
39089     getSlideAnchor : function(){
39090         return this.sanchors[this.position];
39091     },
39092
39093     getAlignAdj : function(){
39094         var cm = this.cmargins;
39095         switch(this.position){
39096             case "west":
39097                 return [0, 0];
39098             break;
39099             case "east":
39100                 return [0, 0];
39101             break;
39102             case "north":
39103                 return [0, 0];
39104             break;
39105             case "south":
39106                 return [0, 0];
39107             break;
39108         }
39109     },
39110
39111     getExpandAdj : function(){
39112         var c = this.collapsedEl, cm = this.cmargins;
39113         switch(this.position){
39114             case "west":
39115                 return [-(cm.right+c.getWidth()+cm.left), 0];
39116             break;
39117             case "east":
39118                 return [cm.right+c.getWidth()+cm.left, 0];
39119             break;
39120             case "north":
39121                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39122             break;
39123             case "south":
39124                 return [0, cm.top+cm.bottom+c.getHeight()];
39125             break;
39126         }
39127     }
39128 });/*
39129  * Based on:
39130  * Ext JS Library 1.1.1
39131  * Copyright(c) 2006-2007, Ext JS, LLC.
39132  *
39133  * Originally Released Under LGPL - original licence link has changed is not relivant.
39134  *
39135  * Fork - LGPL
39136  * <script type="text/javascript">
39137  */
39138 /*
39139  * These classes are private internal classes
39140  */
39141 Roo.bootstrap.layout.Center = function(config){
39142     config.region = "center";
39143     Roo.bootstrap.layout.Region.call(this, config);
39144     this.visible = true;
39145     this.minWidth = config.minWidth || 20;
39146     this.minHeight = config.minHeight || 20;
39147 };
39148
39149 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39150     hide : function(){
39151         // center panel can't be hidden
39152     },
39153     
39154     show : function(){
39155         // center panel can't be hidden
39156     },
39157     
39158     getMinWidth: function(){
39159         return this.minWidth;
39160     },
39161     
39162     getMinHeight: function(){
39163         return this.minHeight;
39164     }
39165 });
39166
39167
39168
39169
39170  
39171
39172
39173
39174
39175
39176
39177 Roo.bootstrap.layout.North = function(config)
39178 {
39179     config.region = 'north';
39180     config.cursor = 'n-resize';
39181     
39182     Roo.bootstrap.layout.Split.call(this, config);
39183     
39184     
39185     if(this.split){
39186         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39187         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39188         this.split.el.addClass("roo-layout-split-v");
39189     }
39190     var size = config.initialSize || config.height;
39191     if(typeof size != "undefined"){
39192         this.el.setHeight(size);
39193     }
39194 };
39195 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39196 {
39197     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39198     
39199     
39200     
39201     getBox : function(){
39202         if(this.collapsed){
39203             return this.collapsedEl.getBox();
39204         }
39205         var box = this.el.getBox();
39206         if(this.split){
39207             box.height += this.split.el.getHeight();
39208         }
39209         return box;
39210     },
39211     
39212     updateBox : function(box){
39213         if(this.split && !this.collapsed){
39214             box.height -= this.split.el.getHeight();
39215             this.split.el.setLeft(box.x);
39216             this.split.el.setTop(box.y+box.height);
39217             this.split.el.setWidth(box.width);
39218         }
39219         if(this.collapsed){
39220             this.updateBody(box.width, null);
39221         }
39222         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39223     }
39224 });
39225
39226
39227
39228
39229
39230 Roo.bootstrap.layout.South = function(config){
39231     config.region = 'south';
39232     config.cursor = 's-resize';
39233     Roo.bootstrap.layout.Split.call(this, config);
39234     if(this.split){
39235         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39236         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39237         this.split.el.addClass("roo-layout-split-v");
39238     }
39239     var size = config.initialSize || config.height;
39240     if(typeof size != "undefined"){
39241         this.el.setHeight(size);
39242     }
39243 };
39244
39245 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39246     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39247     getBox : function(){
39248         if(this.collapsed){
39249             return this.collapsedEl.getBox();
39250         }
39251         var box = this.el.getBox();
39252         if(this.split){
39253             var sh = this.split.el.getHeight();
39254             box.height += sh;
39255             box.y -= sh;
39256         }
39257         return box;
39258     },
39259     
39260     updateBox : function(box){
39261         if(this.split && !this.collapsed){
39262             var sh = this.split.el.getHeight();
39263             box.height -= sh;
39264             box.y += sh;
39265             this.split.el.setLeft(box.x);
39266             this.split.el.setTop(box.y-sh);
39267             this.split.el.setWidth(box.width);
39268         }
39269         if(this.collapsed){
39270             this.updateBody(box.width, null);
39271         }
39272         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39273     }
39274 });
39275
39276 Roo.bootstrap.layout.East = function(config){
39277     config.region = "east";
39278     config.cursor = "e-resize";
39279     Roo.bootstrap.layout.Split.call(this, config);
39280     if(this.split){
39281         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39282         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39283         this.split.el.addClass("roo-layout-split-h");
39284     }
39285     var size = config.initialSize || config.width;
39286     if(typeof size != "undefined"){
39287         this.el.setWidth(size);
39288     }
39289 };
39290 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39291     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39292     getBox : function(){
39293         if(this.collapsed){
39294             return this.collapsedEl.getBox();
39295         }
39296         var box = this.el.getBox();
39297         if(this.split){
39298             var sw = this.split.el.getWidth();
39299             box.width += sw;
39300             box.x -= sw;
39301         }
39302         return box;
39303     },
39304
39305     updateBox : function(box){
39306         if(this.split && !this.collapsed){
39307             var sw = this.split.el.getWidth();
39308             box.width -= sw;
39309             this.split.el.setLeft(box.x);
39310             this.split.el.setTop(box.y);
39311             this.split.el.setHeight(box.height);
39312             box.x += sw;
39313         }
39314         if(this.collapsed){
39315             this.updateBody(null, box.height);
39316         }
39317         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39318     }
39319 });
39320
39321 Roo.bootstrap.layout.West = function(config){
39322     config.region = "west";
39323     config.cursor = "w-resize";
39324     
39325     Roo.bootstrap.layout.Split.call(this, config);
39326     if(this.split){
39327         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39328         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39329         this.split.el.addClass("roo-layout-split-h");
39330     }
39331     
39332 };
39333 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39334     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39335     
39336     onRender: function(ctr, pos)
39337     {
39338         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39339         var size = this.config.initialSize || this.config.width;
39340         if(typeof size != "undefined"){
39341             this.el.setWidth(size);
39342         }
39343     },
39344     
39345     getBox : function(){
39346         if(this.collapsed){
39347             return this.collapsedEl.getBox();
39348         }
39349         var box = this.el.getBox();
39350         if(this.split){
39351             box.width += this.split.el.getWidth();
39352         }
39353         return box;
39354     },
39355     
39356     updateBox : function(box){
39357         if(this.split && !this.collapsed){
39358             var sw = this.split.el.getWidth();
39359             box.width -= sw;
39360             this.split.el.setLeft(box.x+box.width);
39361             this.split.el.setTop(box.y);
39362             this.split.el.setHeight(box.height);
39363         }
39364         if(this.collapsed){
39365             this.updateBody(null, box.height);
39366         }
39367         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39368     }
39369 });Roo.namespace("Roo.bootstrap.panel");/*
39370  * Based on:
39371  * Ext JS Library 1.1.1
39372  * Copyright(c) 2006-2007, Ext JS, LLC.
39373  *
39374  * Originally Released Under LGPL - original licence link has changed is not relivant.
39375  *
39376  * Fork - LGPL
39377  * <script type="text/javascript">
39378  */
39379 /**
39380  * @class Roo.ContentPanel
39381  * @extends Roo.util.Observable
39382  * A basic ContentPanel element.
39383  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39384  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39385  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
39386  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39387  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39388  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39389  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39390  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39391  * @cfg {String} title          The title for this panel
39392  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39393  * @cfg {String} url            Calls {@link #setUrl} with this value
39394  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39395  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39396  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39397  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39398  * @cfg {Boolean} badges render the badges
39399  * @cfg {String} cls  extra classes to use  
39400  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39401
39402  * @constructor
39403  * Create a new ContentPanel.
39404  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39405  * @param {String/Object} config A string to set only the title or a config object
39406  * @param {String} content (optional) Set the HTML content for this panel
39407  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39408  */
39409 Roo.bootstrap.panel.Content = function( config){
39410     
39411     this.tpl = config.tpl || false;
39412     
39413     var el = config.el;
39414     var content = config.content;
39415
39416     if(config.autoCreate){ // xtype is available if this is called from factory
39417         el = Roo.id();
39418     }
39419     this.el = Roo.get(el);
39420     if(!this.el && config && config.autoCreate){
39421         if(typeof config.autoCreate == "object"){
39422             if(!config.autoCreate.id){
39423                 config.autoCreate.id = config.id||el;
39424             }
39425             this.el = Roo.DomHelper.append(document.body,
39426                         config.autoCreate, true);
39427         }else{
39428             var elcfg =  {
39429                 tag: "div",
39430                 cls: (config.cls || '') +
39431                     (config.background ? ' bg-' + config.background : '') +
39432                     " roo-layout-inactive-content",
39433                 id: config.id||el
39434             };
39435             if (config.html) {
39436                 elcfg.html = config.html;
39437                 
39438             }
39439                         
39440             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39441         }
39442     } 
39443     this.closable = false;
39444     this.loaded = false;
39445     this.active = false;
39446    
39447       
39448     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39449         
39450         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39451         
39452         this.wrapEl = this.el; //this.el.wrap();
39453         var ti = [];
39454         if (config.toolbar.items) {
39455             ti = config.toolbar.items ;
39456             delete config.toolbar.items ;
39457         }
39458         
39459         var nitems = [];
39460         this.toolbar.render(this.wrapEl, 'before');
39461         for(var i =0;i < ti.length;i++) {
39462           //  Roo.log(['add child', items[i]]);
39463             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39464         }
39465         this.toolbar.items = nitems;
39466         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39467         delete config.toolbar;
39468         
39469     }
39470     /*
39471     // xtype created footer. - not sure if will work as we normally have to render first..
39472     if (this.footer && !this.footer.el && this.footer.xtype) {
39473         if (!this.wrapEl) {
39474             this.wrapEl = this.el.wrap();
39475         }
39476     
39477         this.footer.container = this.wrapEl.createChild();
39478          
39479         this.footer = Roo.factory(this.footer, Roo);
39480         
39481     }
39482     */
39483     
39484      if(typeof config == "string"){
39485         this.title = config;
39486     }else{
39487         Roo.apply(this, config);
39488     }
39489     
39490     if(this.resizeEl){
39491         this.resizeEl = Roo.get(this.resizeEl, true);
39492     }else{
39493         this.resizeEl = this.el;
39494     }
39495     // handle view.xtype
39496     
39497  
39498     
39499     
39500     this.addEvents({
39501         /**
39502          * @event activate
39503          * Fires when this panel is activated. 
39504          * @param {Roo.ContentPanel} this
39505          */
39506         "activate" : true,
39507         /**
39508          * @event deactivate
39509          * Fires when this panel is activated. 
39510          * @param {Roo.ContentPanel} this
39511          */
39512         "deactivate" : true,
39513
39514         /**
39515          * @event resize
39516          * Fires when this panel is resized if fitToFrame is true.
39517          * @param {Roo.ContentPanel} this
39518          * @param {Number} width The width after any component adjustments
39519          * @param {Number} height The height after any component adjustments
39520          */
39521         "resize" : true,
39522         
39523          /**
39524          * @event render
39525          * Fires when this tab is created
39526          * @param {Roo.ContentPanel} this
39527          */
39528         "render" : true
39529         
39530         
39531         
39532     });
39533     
39534
39535     
39536     
39537     if(this.autoScroll){
39538         this.resizeEl.setStyle("overflow", "auto");
39539     } else {
39540         // fix randome scrolling
39541         //this.el.on('scroll', function() {
39542         //    Roo.log('fix random scolling');
39543         //    this.scrollTo('top',0); 
39544         //});
39545     }
39546     content = content || this.content;
39547     if(content){
39548         this.setContent(content);
39549     }
39550     if(config && config.url){
39551         this.setUrl(this.url, this.params, this.loadOnce);
39552     }
39553     
39554     
39555     
39556     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39557     
39558     if (this.view && typeof(this.view.xtype) != 'undefined') {
39559         this.view.el = this.el.appendChild(document.createElement("div"));
39560         this.view = Roo.factory(this.view); 
39561         this.view.render  &&  this.view.render(false, '');  
39562     }
39563     
39564     
39565     this.fireEvent('render', this);
39566 };
39567
39568 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39569     
39570     cls : '',
39571     background : '',
39572     
39573     tabTip : '',
39574     
39575     setRegion : function(region){
39576         this.region = region;
39577         this.setActiveClass(region && !this.background);
39578     },
39579     
39580     
39581     setActiveClass: function(state)
39582     {
39583         if(state){
39584            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39585            this.el.setStyle('position','relative');
39586         }else{
39587            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39588            this.el.setStyle('position', 'absolute');
39589         } 
39590     },
39591     
39592     /**
39593      * Returns the toolbar for this Panel if one was configured. 
39594      * @return {Roo.Toolbar} 
39595      */
39596     getToolbar : function(){
39597         return this.toolbar;
39598     },
39599     
39600     setActiveState : function(active)
39601     {
39602         this.active = active;
39603         this.setActiveClass(active);
39604         if(!active){
39605             if(this.fireEvent("deactivate", this) === false){
39606                 return false;
39607             }
39608             return true;
39609         }
39610         this.fireEvent("activate", this);
39611         return true;
39612     },
39613     /**
39614      * Updates this panel's element
39615      * @param {String} content The new content
39616      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39617     */
39618     setContent : function(content, loadScripts){
39619         this.el.update(content, loadScripts);
39620     },
39621
39622     ignoreResize : function(w, h){
39623         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39624             return true;
39625         }else{
39626             this.lastSize = {width: w, height: h};
39627             return false;
39628         }
39629     },
39630     /**
39631      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39632      * @return {Roo.UpdateManager} The UpdateManager
39633      */
39634     getUpdateManager : function(){
39635         return this.el.getUpdateManager();
39636     },
39637      /**
39638      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39639      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
39640 <pre><code>
39641 panel.load({
39642     url: "your-url.php",
39643     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39644     callback: yourFunction,
39645     scope: yourObject, //(optional scope)
39646     discardUrl: false,
39647     nocache: false,
39648     text: "Loading...",
39649     timeout: 30,
39650     scripts: false
39651 });
39652 </code></pre>
39653      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39654      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
39655      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
39656      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39657      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
39658      * @return {Roo.ContentPanel} this
39659      */
39660     load : function(){
39661         var um = this.el.getUpdateManager();
39662         um.update.apply(um, arguments);
39663         return this;
39664     },
39665
39666
39667     /**
39668      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
39669      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39670      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
39671      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
39672      * @return {Roo.UpdateManager} The UpdateManager
39673      */
39674     setUrl : function(url, params, loadOnce){
39675         if(this.refreshDelegate){
39676             this.removeListener("activate", this.refreshDelegate);
39677         }
39678         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39679         this.on("activate", this.refreshDelegate);
39680         return this.el.getUpdateManager();
39681     },
39682     
39683     _handleRefresh : function(url, params, loadOnce){
39684         if(!loadOnce || !this.loaded){
39685             var updater = this.el.getUpdateManager();
39686             updater.update(url, params, this._setLoaded.createDelegate(this));
39687         }
39688     },
39689     
39690     _setLoaded : function(){
39691         this.loaded = true;
39692     }, 
39693     
39694     /**
39695      * Returns this panel's id
39696      * @return {String} 
39697      */
39698     getId : function(){
39699         return this.el.id;
39700     },
39701     
39702     /** 
39703      * Returns this panel's element - used by regiosn to add.
39704      * @return {Roo.Element} 
39705      */
39706     getEl : function(){
39707         return this.wrapEl || this.el;
39708     },
39709     
39710    
39711     
39712     adjustForComponents : function(width, height)
39713     {
39714         //Roo.log('adjustForComponents ');
39715         if(this.resizeEl != this.el){
39716             width -= this.el.getFrameWidth('lr');
39717             height -= this.el.getFrameWidth('tb');
39718         }
39719         if(this.toolbar){
39720             var te = this.toolbar.getEl();
39721             te.setWidth(width);
39722             height -= te.getHeight();
39723         }
39724         if(this.footer){
39725             var te = this.footer.getEl();
39726             te.setWidth(width);
39727             height -= te.getHeight();
39728         }
39729         
39730         
39731         if(this.adjustments){
39732             width += this.adjustments[0];
39733             height += this.adjustments[1];
39734         }
39735         return {"width": width, "height": height};
39736     },
39737     
39738     setSize : function(width, height){
39739         if(this.fitToFrame && !this.ignoreResize(width, height)){
39740             if(this.fitContainer && this.resizeEl != this.el){
39741                 this.el.setSize(width, height);
39742             }
39743             var size = this.adjustForComponents(width, height);
39744             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39745             this.fireEvent('resize', this, size.width, size.height);
39746         }
39747     },
39748     
39749     /**
39750      * Returns this panel's title
39751      * @return {String} 
39752      */
39753     getTitle : function(){
39754         
39755         if (typeof(this.title) != 'object') {
39756             return this.title;
39757         }
39758         
39759         var t = '';
39760         for (var k in this.title) {
39761             if (!this.title.hasOwnProperty(k)) {
39762                 continue;
39763             }
39764             
39765             if (k.indexOf('-') >= 0) {
39766                 var s = k.split('-');
39767                 for (var i = 0; i<s.length; i++) {
39768                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39769                 }
39770             } else {
39771                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39772             }
39773         }
39774         return t;
39775     },
39776     
39777     /**
39778      * Set this panel's title
39779      * @param {String} title
39780      */
39781     setTitle : function(title){
39782         this.title = title;
39783         if(this.region){
39784             this.region.updatePanelTitle(this, title);
39785         }
39786     },
39787     
39788     /**
39789      * Returns true is this panel was configured to be closable
39790      * @return {Boolean} 
39791      */
39792     isClosable : function(){
39793         return this.closable;
39794     },
39795     
39796     beforeSlide : function(){
39797         this.el.clip();
39798         this.resizeEl.clip();
39799     },
39800     
39801     afterSlide : function(){
39802         this.el.unclip();
39803         this.resizeEl.unclip();
39804     },
39805     
39806     /**
39807      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39808      *   Will fail silently if the {@link #setUrl} method has not been called.
39809      *   This does not activate the panel, just updates its content.
39810      */
39811     refresh : function(){
39812         if(this.refreshDelegate){
39813            this.loaded = false;
39814            this.refreshDelegate();
39815         }
39816     },
39817     
39818     /**
39819      * Destroys this panel
39820      */
39821     destroy : function(){
39822         this.el.removeAllListeners();
39823         var tempEl = document.createElement("span");
39824         tempEl.appendChild(this.el.dom);
39825         tempEl.innerHTML = "";
39826         this.el.remove();
39827         this.el = null;
39828     },
39829     
39830     /**
39831      * form - if the content panel contains a form - this is a reference to it.
39832      * @type {Roo.form.Form}
39833      */
39834     form : false,
39835     /**
39836      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39837      *    This contains a reference to it.
39838      * @type {Roo.View}
39839      */
39840     view : false,
39841     
39842       /**
39843      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39844      * <pre><code>
39845
39846 layout.addxtype({
39847        xtype : 'Form',
39848        items: [ .... ]
39849    }
39850 );
39851
39852 </code></pre>
39853      * @param {Object} cfg Xtype definition of item to add.
39854      */
39855     
39856     
39857     getChildContainer: function () {
39858         return this.getEl();
39859     }
39860     
39861     
39862     /*
39863         var  ret = new Roo.factory(cfg);
39864         return ret;
39865         
39866         
39867         // add form..
39868         if (cfg.xtype.match(/^Form$/)) {
39869             
39870             var el;
39871             //if (this.footer) {
39872             //    el = this.footer.container.insertSibling(false, 'before');
39873             //} else {
39874                 el = this.el.createChild();
39875             //}
39876
39877             this.form = new  Roo.form.Form(cfg);
39878             
39879             
39880             if ( this.form.allItems.length) {
39881                 this.form.render(el.dom);
39882             }
39883             return this.form;
39884         }
39885         // should only have one of theses..
39886         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39887             // views.. should not be just added - used named prop 'view''
39888             
39889             cfg.el = this.el.appendChild(document.createElement("div"));
39890             // factory?
39891             
39892             var ret = new Roo.factory(cfg);
39893              
39894              ret.render && ret.render(false, ''); // render blank..
39895             this.view = ret;
39896             return ret;
39897         }
39898         return false;
39899     }
39900     \*/
39901 });
39902  
39903 /**
39904  * @class Roo.bootstrap.panel.Grid
39905  * @extends Roo.bootstrap.panel.Content
39906  * @constructor
39907  * Create a new GridPanel.
39908  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39909  * @param {Object} config A the config object
39910   
39911  */
39912
39913
39914
39915 Roo.bootstrap.panel.Grid = function(config)
39916 {
39917     
39918       
39919     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39920         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39921
39922     config.el = this.wrapper;
39923     //this.el = this.wrapper;
39924     
39925       if (config.container) {
39926         // ctor'ed from a Border/panel.grid
39927         
39928         
39929         this.wrapper.setStyle("overflow", "hidden");
39930         this.wrapper.addClass('roo-grid-container');
39931
39932     }
39933     
39934     
39935     if(config.toolbar){
39936         var tool_el = this.wrapper.createChild();    
39937         this.toolbar = Roo.factory(config.toolbar);
39938         var ti = [];
39939         if (config.toolbar.items) {
39940             ti = config.toolbar.items ;
39941             delete config.toolbar.items ;
39942         }
39943         
39944         var nitems = [];
39945         this.toolbar.render(tool_el);
39946         for(var i =0;i < ti.length;i++) {
39947           //  Roo.log(['add child', items[i]]);
39948             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39949         }
39950         this.toolbar.items = nitems;
39951         
39952         delete config.toolbar;
39953     }
39954     
39955     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39956     config.grid.scrollBody = true;;
39957     config.grid.monitorWindowResize = false; // turn off autosizing
39958     config.grid.autoHeight = false;
39959     config.grid.autoWidth = false;
39960     
39961     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39962     
39963     if (config.background) {
39964         // render grid on panel activation (if panel background)
39965         this.on('activate', function(gp) {
39966             if (!gp.grid.rendered) {
39967                 gp.grid.render(this.wrapper);
39968                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39969             }
39970         });
39971             
39972     } else {
39973         this.grid.render(this.wrapper);
39974         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39975
39976     }
39977     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39978     // ??? needed ??? config.el = this.wrapper;
39979     
39980     
39981     
39982   
39983     // xtype created footer. - not sure if will work as we normally have to render first..
39984     if (this.footer && !this.footer.el && this.footer.xtype) {
39985         
39986         var ctr = this.grid.getView().getFooterPanel(true);
39987         this.footer.dataSource = this.grid.dataSource;
39988         this.footer = Roo.factory(this.footer, Roo);
39989         this.footer.render(ctr);
39990         
39991     }
39992     
39993     
39994     
39995     
39996      
39997 };
39998
39999 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40000     getId : function(){
40001         return this.grid.id;
40002     },
40003     
40004     /**
40005      * Returns the grid for this panel
40006      * @return {Roo.bootstrap.Table} 
40007      */
40008     getGrid : function(){
40009         return this.grid;    
40010     },
40011     
40012     setSize : function(width, height){
40013         if(!this.ignoreResize(width, height)){
40014             var grid = this.grid;
40015             var size = this.adjustForComponents(width, height);
40016             // tfoot is not a footer?
40017           
40018             
40019             var gridel = grid.getGridEl();
40020             gridel.setSize(size.width, size.height);
40021             
40022             var tbd = grid.getGridEl().select('tbody', true).first();
40023             var thd = grid.getGridEl().select('thead',true).first();
40024             var tbf= grid.getGridEl().select('tfoot', true).first();
40025
40026             if (tbf) {
40027                 size.height -= thd.getHeight();
40028             }
40029             if (thd) {
40030                 size.height -= thd.getHeight();
40031             }
40032             
40033             tbd.setSize(size.width, size.height );
40034             // this is for the account management tab -seems to work there.
40035             var thd = grid.getGridEl().select('thead',true).first();
40036             //if (tbd) {
40037             //    tbd.setSize(size.width, size.height - thd.getHeight());
40038             //}
40039              
40040             grid.autoSize();
40041         }
40042     },
40043      
40044     
40045     
40046     beforeSlide : function(){
40047         this.grid.getView().scroller.clip();
40048     },
40049     
40050     afterSlide : function(){
40051         this.grid.getView().scroller.unclip();
40052     },
40053     
40054     destroy : function(){
40055         this.grid.destroy();
40056         delete this.grid;
40057         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40058     }
40059 });
40060
40061 /**
40062  * @class Roo.bootstrap.panel.Nest
40063  * @extends Roo.bootstrap.panel.Content
40064  * @constructor
40065  * Create a new Panel, that can contain a layout.Border.
40066  * 
40067  * 
40068  * @param {Roo.BorderLayout} layout The layout for this panel
40069  * @param {String/Object} config A string to set only the title or a config object
40070  */
40071 Roo.bootstrap.panel.Nest = function(config)
40072 {
40073     // construct with only one argument..
40074     /* FIXME - implement nicer consturctors
40075     if (layout.layout) {
40076         config = layout;
40077         layout = config.layout;
40078         delete config.layout;
40079     }
40080     if (layout.xtype && !layout.getEl) {
40081         // then layout needs constructing..
40082         layout = Roo.factory(layout, Roo);
40083     }
40084     */
40085     
40086     config.el =  config.layout.getEl();
40087     
40088     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40089     
40090     config.layout.monitorWindowResize = false; // turn off autosizing
40091     this.layout = config.layout;
40092     this.layout.getEl().addClass("roo-layout-nested-layout");
40093     this.layout.parent = this;
40094     
40095     
40096     
40097     
40098 };
40099
40100 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40101
40102     setSize : function(width, height){
40103         if(!this.ignoreResize(width, height)){
40104             var size = this.adjustForComponents(width, height);
40105             var el = this.layout.getEl();
40106             if (size.height < 1) {
40107                 el.setWidth(size.width);   
40108             } else {
40109                 el.setSize(size.width, size.height);
40110             }
40111             var touch = el.dom.offsetWidth;
40112             this.layout.layout();
40113             // ie requires a double layout on the first pass
40114             if(Roo.isIE && !this.initialized){
40115                 this.initialized = true;
40116                 this.layout.layout();
40117             }
40118         }
40119     },
40120     
40121     // activate all subpanels if not currently active..
40122     
40123     setActiveState : function(active){
40124         this.active = active;
40125         this.setActiveClass(active);
40126         
40127         if(!active){
40128             this.fireEvent("deactivate", this);
40129             return;
40130         }
40131         
40132         this.fireEvent("activate", this);
40133         // not sure if this should happen before or after..
40134         if (!this.layout) {
40135             return; // should not happen..
40136         }
40137         var reg = false;
40138         for (var r in this.layout.regions) {
40139             reg = this.layout.getRegion(r);
40140             if (reg.getActivePanel()) {
40141                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40142                 reg.setActivePanel(reg.getActivePanel());
40143                 continue;
40144             }
40145             if (!reg.panels.length) {
40146                 continue;
40147             }
40148             reg.showPanel(reg.getPanel(0));
40149         }
40150         
40151         
40152         
40153         
40154     },
40155     
40156     /**
40157      * Returns the nested BorderLayout for this panel
40158      * @return {Roo.BorderLayout} 
40159      */
40160     getLayout : function(){
40161         return this.layout;
40162     },
40163     
40164      /**
40165      * Adds a xtype elements to the layout of the nested panel
40166      * <pre><code>
40167
40168 panel.addxtype({
40169        xtype : 'ContentPanel',
40170        region: 'west',
40171        items: [ .... ]
40172    }
40173 );
40174
40175 panel.addxtype({
40176         xtype : 'NestedLayoutPanel',
40177         region: 'west',
40178         layout: {
40179            center: { },
40180            west: { }   
40181         },
40182         items : [ ... list of content panels or nested layout panels.. ]
40183    }
40184 );
40185 </code></pre>
40186      * @param {Object} cfg Xtype definition of item to add.
40187      */
40188     addxtype : function(cfg) {
40189         return this.layout.addxtype(cfg);
40190     
40191     }
40192 });/*
40193  * Based on:
40194  * Ext JS Library 1.1.1
40195  * Copyright(c) 2006-2007, Ext JS, LLC.
40196  *
40197  * Originally Released Under LGPL - original licence link has changed is not relivant.
40198  *
40199  * Fork - LGPL
40200  * <script type="text/javascript">
40201  */
40202 /**
40203  * @class Roo.TabPanel
40204  * @extends Roo.util.Observable
40205  * A lightweight tab container.
40206  * <br><br>
40207  * Usage:
40208  * <pre><code>
40209 // basic tabs 1, built from existing content
40210 var tabs = new Roo.TabPanel("tabs1");
40211 tabs.addTab("script", "View Script");
40212 tabs.addTab("markup", "View Markup");
40213 tabs.activate("script");
40214
40215 // more advanced tabs, built from javascript
40216 var jtabs = new Roo.TabPanel("jtabs");
40217 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40218
40219 // set up the UpdateManager
40220 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40221 var updater = tab2.getUpdateManager();
40222 updater.setDefaultUrl("ajax1.htm");
40223 tab2.on('activate', updater.refresh, updater, true);
40224
40225 // Use setUrl for Ajax loading
40226 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40227 tab3.setUrl("ajax2.htm", null, true);
40228
40229 // Disabled tab
40230 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40231 tab4.disable();
40232
40233 jtabs.activate("jtabs-1");
40234  * </code></pre>
40235  * @constructor
40236  * Create a new TabPanel.
40237  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40238  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40239  */
40240 Roo.bootstrap.panel.Tabs = function(config){
40241     /**
40242     * The container element for this TabPanel.
40243     * @type Roo.Element
40244     */
40245     this.el = Roo.get(config.el);
40246     delete config.el;
40247     if(config){
40248         if(typeof config == "boolean"){
40249             this.tabPosition = config ? "bottom" : "top";
40250         }else{
40251             Roo.apply(this, config);
40252         }
40253     }
40254     
40255     if(this.tabPosition == "bottom"){
40256         // if tabs are at the bottom = create the body first.
40257         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40258         this.el.addClass("roo-tabs-bottom");
40259     }
40260     // next create the tabs holders
40261     
40262     if (this.tabPosition == "west"){
40263         
40264         var reg = this.region; // fake it..
40265         while (reg) {
40266             if (!reg.mgr.parent) {
40267                 break;
40268             }
40269             reg = reg.mgr.parent.region;
40270         }
40271         Roo.log("got nest?");
40272         Roo.log(reg);
40273         if (reg.mgr.getRegion('west')) {
40274             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40275             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40276             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40277             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40278             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40279         
40280             
40281         }
40282         
40283         
40284     } else {
40285      
40286         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40287         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40288         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40289         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40290     }
40291     
40292     
40293     if(Roo.isIE){
40294         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40295     }
40296     
40297     // finally - if tabs are at the top, then create the body last..
40298     if(this.tabPosition != "bottom"){
40299         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40300          * @type Roo.Element
40301          */
40302         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40303         this.el.addClass("roo-tabs-top");
40304     }
40305     this.items = [];
40306
40307     this.bodyEl.setStyle("position", "relative");
40308
40309     this.active = null;
40310     this.activateDelegate = this.activate.createDelegate(this);
40311
40312     this.addEvents({
40313         /**
40314          * @event tabchange
40315          * Fires when the active tab changes
40316          * @param {Roo.TabPanel} this
40317          * @param {Roo.TabPanelItem} activePanel The new active tab
40318          */
40319         "tabchange": true,
40320         /**
40321          * @event beforetabchange
40322          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40323          * @param {Roo.TabPanel} this
40324          * @param {Object} e Set cancel to true on this object to cancel the tab change
40325          * @param {Roo.TabPanelItem} tab The tab being changed to
40326          */
40327         "beforetabchange" : true
40328     });
40329
40330     Roo.EventManager.onWindowResize(this.onResize, this);
40331     this.cpad = this.el.getPadding("lr");
40332     this.hiddenCount = 0;
40333
40334
40335     // toolbar on the tabbar support...
40336     if (this.toolbar) {
40337         alert("no toolbar support yet");
40338         this.toolbar  = false;
40339         /*
40340         var tcfg = this.toolbar;
40341         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40342         this.toolbar = new Roo.Toolbar(tcfg);
40343         if (Roo.isSafari) {
40344             var tbl = tcfg.container.child('table', true);
40345             tbl.setAttribute('width', '100%');
40346         }
40347         */
40348         
40349     }
40350    
40351
40352
40353     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40354 };
40355
40356 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40357     /*
40358      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40359      */
40360     tabPosition : "top",
40361     /*
40362      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40363      */
40364     currentTabWidth : 0,
40365     /*
40366      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40367      */
40368     minTabWidth : 40,
40369     /*
40370      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40371      */
40372     maxTabWidth : 250,
40373     /*
40374      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40375      */
40376     preferredTabWidth : 175,
40377     /*
40378      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40379      */
40380     resizeTabs : false,
40381     /*
40382      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40383      */
40384     monitorResize : true,
40385     /*
40386      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40387      */
40388     toolbar : false,  // set by caller..
40389     
40390     region : false, /// set by caller
40391     
40392     disableTooltips : true, // not used yet...
40393
40394     /**
40395      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40396      * @param {String} id The id of the div to use <b>or create</b>
40397      * @param {String} text The text for the tab
40398      * @param {String} content (optional) Content to put in the TabPanelItem body
40399      * @param {Boolean} closable (optional) True to create a close icon on the tab
40400      * @return {Roo.TabPanelItem} The created TabPanelItem
40401      */
40402     addTab : function(id, text, content, closable, tpl)
40403     {
40404         var item = new Roo.bootstrap.panel.TabItem({
40405             panel: this,
40406             id : id,
40407             text : text,
40408             closable : closable,
40409             tpl : tpl
40410         });
40411         this.addTabItem(item);
40412         if(content){
40413             item.setContent(content);
40414         }
40415         return item;
40416     },
40417
40418     /**
40419      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40420      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40421      * @return {Roo.TabPanelItem}
40422      */
40423     getTab : function(id){
40424         return this.items[id];
40425     },
40426
40427     /**
40428      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40429      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40430      */
40431     hideTab : function(id){
40432         var t = this.items[id];
40433         if(!t.isHidden()){
40434            t.setHidden(true);
40435            this.hiddenCount++;
40436            this.autoSizeTabs();
40437         }
40438     },
40439
40440     /**
40441      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40442      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40443      */
40444     unhideTab : function(id){
40445         var t = this.items[id];
40446         if(t.isHidden()){
40447            t.setHidden(false);
40448            this.hiddenCount--;
40449            this.autoSizeTabs();
40450         }
40451     },
40452
40453     /**
40454      * Adds an existing {@link Roo.TabPanelItem}.
40455      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40456      */
40457     addTabItem : function(item)
40458     {
40459         this.items[item.id] = item;
40460         this.items.push(item);
40461         this.autoSizeTabs();
40462       //  if(this.resizeTabs){
40463     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40464   //         this.autoSizeTabs();
40465 //        }else{
40466 //            item.autoSize();
40467        // }
40468     },
40469
40470     /**
40471      * Removes a {@link Roo.TabPanelItem}.
40472      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40473      */
40474     removeTab : function(id){
40475         var items = this.items;
40476         var tab = items[id];
40477         if(!tab) { return; }
40478         var index = items.indexOf(tab);
40479         if(this.active == tab && items.length > 1){
40480             var newTab = this.getNextAvailable(index);
40481             if(newTab) {
40482                 newTab.activate();
40483             }
40484         }
40485         this.stripEl.dom.removeChild(tab.pnode.dom);
40486         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40487             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40488         }
40489         items.splice(index, 1);
40490         delete this.items[tab.id];
40491         tab.fireEvent("close", tab);
40492         tab.purgeListeners();
40493         this.autoSizeTabs();
40494     },
40495
40496     getNextAvailable : function(start){
40497         var items = this.items;
40498         var index = start;
40499         // look for a next tab that will slide over to
40500         // replace the one being removed
40501         while(index < items.length){
40502             var item = items[++index];
40503             if(item && !item.isHidden()){
40504                 return item;
40505             }
40506         }
40507         // if one isn't found select the previous tab (on the left)
40508         index = start;
40509         while(index >= 0){
40510             var item = items[--index];
40511             if(item && !item.isHidden()){
40512                 return item;
40513             }
40514         }
40515         return null;
40516     },
40517
40518     /**
40519      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40520      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40521      */
40522     disableTab : function(id){
40523         var tab = this.items[id];
40524         if(tab && this.active != tab){
40525             tab.disable();
40526         }
40527     },
40528
40529     /**
40530      * Enables a {@link Roo.TabPanelItem} that is disabled.
40531      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40532      */
40533     enableTab : function(id){
40534         var tab = this.items[id];
40535         tab.enable();
40536     },
40537
40538     /**
40539      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40540      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40541      * @return {Roo.TabPanelItem} The TabPanelItem.
40542      */
40543     activate : function(id)
40544     {
40545         //Roo.log('activite:'  + id);
40546         
40547         var tab = this.items[id];
40548         if(!tab){
40549             return null;
40550         }
40551         if(tab == this.active || tab.disabled){
40552             return tab;
40553         }
40554         var e = {};
40555         this.fireEvent("beforetabchange", this, e, tab);
40556         if(e.cancel !== true && !tab.disabled){
40557             if(this.active){
40558                 this.active.hide();
40559             }
40560             this.active = this.items[id];
40561             this.active.show();
40562             this.fireEvent("tabchange", this, this.active);
40563         }
40564         return tab;
40565     },
40566
40567     /**
40568      * Gets the active {@link Roo.TabPanelItem}.
40569      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40570      */
40571     getActiveTab : function(){
40572         return this.active;
40573     },
40574
40575     /**
40576      * Updates the tab body element to fit the height of the container element
40577      * for overflow scrolling
40578      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40579      */
40580     syncHeight : function(targetHeight){
40581         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40582         var bm = this.bodyEl.getMargins();
40583         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40584         this.bodyEl.setHeight(newHeight);
40585         return newHeight;
40586     },
40587
40588     onResize : function(){
40589         if(this.monitorResize){
40590             this.autoSizeTabs();
40591         }
40592     },
40593
40594     /**
40595      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40596      */
40597     beginUpdate : function(){
40598         this.updating = true;
40599     },
40600
40601     /**
40602      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40603      */
40604     endUpdate : function(){
40605         this.updating = false;
40606         this.autoSizeTabs();
40607     },
40608
40609     /**
40610      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40611      */
40612     autoSizeTabs : function()
40613     {
40614         var count = this.items.length;
40615         var vcount = count - this.hiddenCount;
40616         
40617         if (vcount < 2) {
40618             this.stripEl.hide();
40619         } else {
40620             this.stripEl.show();
40621         }
40622         
40623         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40624             return;
40625         }
40626         
40627         
40628         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40629         var availWidth = Math.floor(w / vcount);
40630         var b = this.stripBody;
40631         if(b.getWidth() > w){
40632             var tabs = this.items;
40633             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40634             if(availWidth < this.minTabWidth){
40635                 /*if(!this.sleft){    // incomplete scrolling code
40636                     this.createScrollButtons();
40637                 }
40638                 this.showScroll();
40639                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40640             }
40641         }else{
40642             if(this.currentTabWidth < this.preferredTabWidth){
40643                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40644             }
40645         }
40646     },
40647
40648     /**
40649      * Returns the number of tabs in this TabPanel.
40650      * @return {Number}
40651      */
40652      getCount : function(){
40653          return this.items.length;
40654      },
40655
40656     /**
40657      * Resizes all the tabs to the passed width
40658      * @param {Number} The new width
40659      */
40660     setTabWidth : function(width){
40661         this.currentTabWidth = width;
40662         for(var i = 0, len = this.items.length; i < len; i++) {
40663                 if(!this.items[i].isHidden()) {
40664                 this.items[i].setWidth(width);
40665             }
40666         }
40667     },
40668
40669     /**
40670      * Destroys this TabPanel
40671      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40672      */
40673     destroy : function(removeEl){
40674         Roo.EventManager.removeResizeListener(this.onResize, this);
40675         for(var i = 0, len = this.items.length; i < len; i++){
40676             this.items[i].purgeListeners();
40677         }
40678         if(removeEl === true){
40679             this.el.update("");
40680             this.el.remove();
40681         }
40682     },
40683     
40684     createStrip : function(container)
40685     {
40686         var strip = document.createElement("nav");
40687         strip.className = Roo.bootstrap.version == 4 ?
40688             "navbar-light bg-light" : 
40689             "navbar navbar-default"; //"x-tabs-wrap";
40690         container.appendChild(strip);
40691         return strip;
40692     },
40693     
40694     createStripList : function(strip)
40695     {
40696         // div wrapper for retard IE
40697         // returns the "tr" element.
40698         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40699         //'<div class="x-tabs-strip-wrap">'+
40700           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40701           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40702         return strip.firstChild; //.firstChild.firstChild.firstChild;
40703     },
40704     createBody : function(container)
40705     {
40706         var body = document.createElement("div");
40707         Roo.id(body, "tab-body");
40708         //Roo.fly(body).addClass("x-tabs-body");
40709         Roo.fly(body).addClass("tab-content");
40710         container.appendChild(body);
40711         return body;
40712     },
40713     createItemBody :function(bodyEl, id){
40714         var body = Roo.getDom(id);
40715         if(!body){
40716             body = document.createElement("div");
40717             body.id = id;
40718         }
40719         //Roo.fly(body).addClass("x-tabs-item-body");
40720         Roo.fly(body).addClass("tab-pane");
40721          bodyEl.insertBefore(body, bodyEl.firstChild);
40722         return body;
40723     },
40724     /** @private */
40725     createStripElements :  function(stripEl, text, closable, tpl)
40726     {
40727         var td = document.createElement("li"); // was td..
40728         td.className = 'nav-item';
40729         
40730         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40731         
40732         
40733         stripEl.appendChild(td);
40734         /*if(closable){
40735             td.className = "x-tabs-closable";
40736             if(!this.closeTpl){
40737                 this.closeTpl = new Roo.Template(
40738                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40739                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40740                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40741                 );
40742             }
40743             var el = this.closeTpl.overwrite(td, {"text": text});
40744             var close = el.getElementsByTagName("div")[0];
40745             var inner = el.getElementsByTagName("em")[0];
40746             return {"el": el, "close": close, "inner": inner};
40747         } else {
40748         */
40749         // not sure what this is..
40750 //            if(!this.tabTpl){
40751                 //this.tabTpl = new Roo.Template(
40752                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40753                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40754                 //);
40755 //                this.tabTpl = new Roo.Template(
40756 //                   '<a href="#">' +
40757 //                   '<span unselectable="on"' +
40758 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40759 //                            ' >{text}</span></a>'
40760 //                );
40761 //                
40762 //            }
40763
40764
40765             var template = tpl || this.tabTpl || false;
40766             
40767             if(!template){
40768                 template =  new Roo.Template(
40769                         Roo.bootstrap.version == 4 ? 
40770                             (
40771                                 '<a class="nav-link" href="#" unselectable="on"' +
40772                                      (this.disableTooltips ? '' : ' title="{text}"') +
40773                                      ' >{text}</a>'
40774                             ) : (
40775                                 '<a class="nav-link" href="#">' +
40776                                 '<span unselectable="on"' +
40777                                          (this.disableTooltips ? '' : ' title="{text}"') +
40778                                     ' >{text}</span></a>'
40779                             )
40780                 );
40781             }
40782             
40783             switch (typeof(template)) {
40784                 case 'object' :
40785                     break;
40786                 case 'string' :
40787                     template = new Roo.Template(template);
40788                     break;
40789                 default :
40790                     break;
40791             }
40792             
40793             var el = template.overwrite(td, {"text": text});
40794             
40795             var inner = el.getElementsByTagName("span")[0];
40796             
40797             return {"el": el, "inner": inner};
40798             
40799     }
40800         
40801     
40802 });
40803
40804 /**
40805  * @class Roo.TabPanelItem
40806  * @extends Roo.util.Observable
40807  * Represents an individual item (tab plus body) in a TabPanel.
40808  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40809  * @param {String} id The id of this TabPanelItem
40810  * @param {String} text The text for the tab of this TabPanelItem
40811  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40812  */
40813 Roo.bootstrap.panel.TabItem = function(config){
40814     /**
40815      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40816      * @type Roo.TabPanel
40817      */
40818     this.tabPanel = config.panel;
40819     /**
40820      * The id for this TabPanelItem
40821      * @type String
40822      */
40823     this.id = config.id;
40824     /** @private */
40825     this.disabled = false;
40826     /** @private */
40827     this.text = config.text;
40828     /** @private */
40829     this.loaded = false;
40830     this.closable = config.closable;
40831
40832     /**
40833      * The body element for this TabPanelItem.
40834      * @type Roo.Element
40835      */
40836     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40837     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40838     this.bodyEl.setStyle("display", "block");
40839     this.bodyEl.setStyle("zoom", "1");
40840     //this.hideAction();
40841
40842     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40843     /** @private */
40844     this.el = Roo.get(els.el);
40845     this.inner = Roo.get(els.inner, true);
40846      this.textEl = Roo.bootstrap.version == 4 ?
40847         this.el : Roo.get(this.el.dom.firstChild, true);
40848
40849     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40850     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40851
40852     
40853 //    this.el.on("mousedown", this.onTabMouseDown, this);
40854     this.el.on("click", this.onTabClick, this);
40855     /** @private */
40856     if(config.closable){
40857         var c = Roo.get(els.close, true);
40858         c.dom.title = this.closeText;
40859         c.addClassOnOver("close-over");
40860         c.on("click", this.closeClick, this);
40861      }
40862
40863     this.addEvents({
40864          /**
40865          * @event activate
40866          * Fires when this tab becomes the active tab.
40867          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40868          * @param {Roo.TabPanelItem} this
40869          */
40870         "activate": true,
40871         /**
40872          * @event beforeclose
40873          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40874          * @param {Roo.TabPanelItem} this
40875          * @param {Object} e Set cancel to true on this object to cancel the close.
40876          */
40877         "beforeclose": true,
40878         /**
40879          * @event close
40880          * Fires when this tab is closed.
40881          * @param {Roo.TabPanelItem} this
40882          */
40883          "close": true,
40884         /**
40885          * @event deactivate
40886          * Fires when this tab is no longer the active tab.
40887          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40888          * @param {Roo.TabPanelItem} this
40889          */
40890          "deactivate" : true
40891     });
40892     this.hidden = false;
40893
40894     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40895 };
40896
40897 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40898            {
40899     purgeListeners : function(){
40900        Roo.util.Observable.prototype.purgeListeners.call(this);
40901        this.el.removeAllListeners();
40902     },
40903     /**
40904      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40905      */
40906     show : function(){
40907         this.status_node.addClass("active");
40908         this.showAction();
40909         if(Roo.isOpera){
40910             this.tabPanel.stripWrap.repaint();
40911         }
40912         this.fireEvent("activate", this.tabPanel, this);
40913     },
40914
40915     /**
40916      * Returns true if this tab is the active tab.
40917      * @return {Boolean}
40918      */
40919     isActive : function(){
40920         return this.tabPanel.getActiveTab() == this;
40921     },
40922
40923     /**
40924      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40925      */
40926     hide : function(){
40927         this.status_node.removeClass("active");
40928         this.hideAction();
40929         this.fireEvent("deactivate", this.tabPanel, this);
40930     },
40931
40932     hideAction : function(){
40933         this.bodyEl.hide();
40934         this.bodyEl.setStyle("position", "absolute");
40935         this.bodyEl.setLeft("-20000px");
40936         this.bodyEl.setTop("-20000px");
40937     },
40938
40939     showAction : function(){
40940         this.bodyEl.setStyle("position", "relative");
40941         this.bodyEl.setTop("");
40942         this.bodyEl.setLeft("");
40943         this.bodyEl.show();
40944     },
40945
40946     /**
40947      * Set the tooltip for the tab.
40948      * @param {String} tooltip The tab's tooltip
40949      */
40950     setTooltip : function(text){
40951         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40952             this.textEl.dom.qtip = text;
40953             this.textEl.dom.removeAttribute('title');
40954         }else{
40955             this.textEl.dom.title = text;
40956         }
40957     },
40958
40959     onTabClick : function(e){
40960         e.preventDefault();
40961         this.tabPanel.activate(this.id);
40962     },
40963
40964     onTabMouseDown : function(e){
40965         e.preventDefault();
40966         this.tabPanel.activate(this.id);
40967     },
40968 /*
40969     getWidth : function(){
40970         return this.inner.getWidth();
40971     },
40972
40973     setWidth : function(width){
40974         var iwidth = width - this.linode.getPadding("lr");
40975         this.inner.setWidth(iwidth);
40976         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40977         this.linode.setWidth(width);
40978     },
40979 */
40980     /**
40981      * Show or hide the tab
40982      * @param {Boolean} hidden True to hide or false to show.
40983      */
40984     setHidden : function(hidden){
40985         this.hidden = hidden;
40986         this.linode.setStyle("display", hidden ? "none" : "");
40987     },
40988
40989     /**
40990      * Returns true if this tab is "hidden"
40991      * @return {Boolean}
40992      */
40993     isHidden : function(){
40994         return this.hidden;
40995     },
40996
40997     /**
40998      * Returns the text for this tab
40999      * @return {String}
41000      */
41001     getText : function(){
41002         return this.text;
41003     },
41004     /*
41005     autoSize : function(){
41006         //this.el.beginMeasure();
41007         this.textEl.setWidth(1);
41008         /*
41009          *  #2804 [new] Tabs in Roojs
41010          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41011          */
41012         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41013         //this.el.endMeasure();
41014     //},
41015
41016     /**
41017      * Sets the text for the tab (Note: this also sets the tooltip text)
41018      * @param {String} text The tab's text and tooltip
41019      */
41020     setText : function(text){
41021         this.text = text;
41022         this.textEl.update(text);
41023         this.setTooltip(text);
41024         //if(!this.tabPanel.resizeTabs){
41025         //    this.autoSize();
41026         //}
41027     },
41028     /**
41029      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41030      */
41031     activate : function(){
41032         this.tabPanel.activate(this.id);
41033     },
41034
41035     /**
41036      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41037      */
41038     disable : function(){
41039         if(this.tabPanel.active != this){
41040             this.disabled = true;
41041             this.status_node.addClass("disabled");
41042         }
41043     },
41044
41045     /**
41046      * Enables this TabPanelItem if it was previously disabled.
41047      */
41048     enable : function(){
41049         this.disabled = false;
41050         this.status_node.removeClass("disabled");
41051     },
41052
41053     /**
41054      * Sets the content for this TabPanelItem.
41055      * @param {String} content The content
41056      * @param {Boolean} loadScripts true to look for and load scripts
41057      */
41058     setContent : function(content, loadScripts){
41059         this.bodyEl.update(content, loadScripts);
41060     },
41061
41062     /**
41063      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41064      * @return {Roo.UpdateManager} The UpdateManager
41065      */
41066     getUpdateManager : function(){
41067         return this.bodyEl.getUpdateManager();
41068     },
41069
41070     /**
41071      * Set a URL to be used to load the content for this TabPanelItem.
41072      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41073      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
41074      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
41075      * @return {Roo.UpdateManager} The UpdateManager
41076      */
41077     setUrl : function(url, params, loadOnce){
41078         if(this.refreshDelegate){
41079             this.un('activate', this.refreshDelegate);
41080         }
41081         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41082         this.on("activate", this.refreshDelegate);
41083         return this.bodyEl.getUpdateManager();
41084     },
41085
41086     /** @private */
41087     _handleRefresh : function(url, params, loadOnce){
41088         if(!loadOnce || !this.loaded){
41089             var updater = this.bodyEl.getUpdateManager();
41090             updater.update(url, params, this._setLoaded.createDelegate(this));
41091         }
41092     },
41093
41094     /**
41095      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41096      *   Will fail silently if the setUrl method has not been called.
41097      *   This does not activate the panel, just updates its content.
41098      */
41099     refresh : function(){
41100         if(this.refreshDelegate){
41101            this.loaded = false;
41102            this.refreshDelegate();
41103         }
41104     },
41105
41106     /** @private */
41107     _setLoaded : function(){
41108         this.loaded = true;
41109     },
41110
41111     /** @private */
41112     closeClick : function(e){
41113         var o = {};
41114         e.stopEvent();
41115         this.fireEvent("beforeclose", this, o);
41116         if(o.cancel !== true){
41117             this.tabPanel.removeTab(this.id);
41118         }
41119     },
41120     /**
41121      * The text displayed in the tooltip for the close icon.
41122      * @type String
41123      */
41124     closeText : "Close this tab"
41125 });
41126 /**
41127 *    This script refer to:
41128 *    Title: International Telephone Input
41129 *    Author: Jack O'Connor
41130 *    Code version:  v12.1.12
41131 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41132 **/
41133
41134 Roo.bootstrap.PhoneInputData = function() {
41135     var d = [
41136       [
41137         "Afghanistan (‫افغانستان‬‎)",
41138         "af",
41139         "93"
41140       ],
41141       [
41142         "Albania (Shqipëri)",
41143         "al",
41144         "355"
41145       ],
41146       [
41147         "Algeria (‫الجزائر‬‎)",
41148         "dz",
41149         "213"
41150       ],
41151       [
41152         "American Samoa",
41153         "as",
41154         "1684"
41155       ],
41156       [
41157         "Andorra",
41158         "ad",
41159         "376"
41160       ],
41161       [
41162         "Angola",
41163         "ao",
41164         "244"
41165       ],
41166       [
41167         "Anguilla",
41168         "ai",
41169         "1264"
41170       ],
41171       [
41172         "Antigua and Barbuda",
41173         "ag",
41174         "1268"
41175       ],
41176       [
41177         "Argentina",
41178         "ar",
41179         "54"
41180       ],
41181       [
41182         "Armenia (Հայաստան)",
41183         "am",
41184         "374"
41185       ],
41186       [
41187         "Aruba",
41188         "aw",
41189         "297"
41190       ],
41191       [
41192         "Australia",
41193         "au",
41194         "61",
41195         0
41196       ],
41197       [
41198         "Austria (Österreich)",
41199         "at",
41200         "43"
41201       ],
41202       [
41203         "Azerbaijan (Azərbaycan)",
41204         "az",
41205         "994"
41206       ],
41207       [
41208         "Bahamas",
41209         "bs",
41210         "1242"
41211       ],
41212       [
41213         "Bahrain (‫البحرين‬‎)",
41214         "bh",
41215         "973"
41216       ],
41217       [
41218         "Bangladesh (বাংলাদেশ)",
41219         "bd",
41220         "880"
41221       ],
41222       [
41223         "Barbados",
41224         "bb",
41225         "1246"
41226       ],
41227       [
41228         "Belarus (Беларусь)",
41229         "by",
41230         "375"
41231       ],
41232       [
41233         "Belgium (België)",
41234         "be",
41235         "32"
41236       ],
41237       [
41238         "Belize",
41239         "bz",
41240         "501"
41241       ],
41242       [
41243         "Benin (Bénin)",
41244         "bj",
41245         "229"
41246       ],
41247       [
41248         "Bermuda",
41249         "bm",
41250         "1441"
41251       ],
41252       [
41253         "Bhutan (འབྲུག)",
41254         "bt",
41255         "975"
41256       ],
41257       [
41258         "Bolivia",
41259         "bo",
41260         "591"
41261       ],
41262       [
41263         "Bosnia and Herzegovina (Босна и Херцеговина)",
41264         "ba",
41265         "387"
41266       ],
41267       [
41268         "Botswana",
41269         "bw",
41270         "267"
41271       ],
41272       [
41273         "Brazil (Brasil)",
41274         "br",
41275         "55"
41276       ],
41277       [
41278         "British Indian Ocean Territory",
41279         "io",
41280         "246"
41281       ],
41282       [
41283         "British Virgin Islands",
41284         "vg",
41285         "1284"
41286       ],
41287       [
41288         "Brunei",
41289         "bn",
41290         "673"
41291       ],
41292       [
41293         "Bulgaria (България)",
41294         "bg",
41295         "359"
41296       ],
41297       [
41298         "Burkina Faso",
41299         "bf",
41300         "226"
41301       ],
41302       [
41303         "Burundi (Uburundi)",
41304         "bi",
41305         "257"
41306       ],
41307       [
41308         "Cambodia (កម្ពុជា)",
41309         "kh",
41310         "855"
41311       ],
41312       [
41313         "Cameroon (Cameroun)",
41314         "cm",
41315         "237"
41316       ],
41317       [
41318         "Canada",
41319         "ca",
41320         "1",
41321         1,
41322         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
41323       ],
41324       [
41325         "Cape Verde (Kabu Verdi)",
41326         "cv",
41327         "238"
41328       ],
41329       [
41330         "Caribbean Netherlands",
41331         "bq",
41332         "599",
41333         1
41334       ],
41335       [
41336         "Cayman Islands",
41337         "ky",
41338         "1345"
41339       ],
41340       [
41341         "Central African Republic (République centrafricaine)",
41342         "cf",
41343         "236"
41344       ],
41345       [
41346         "Chad (Tchad)",
41347         "td",
41348         "235"
41349       ],
41350       [
41351         "Chile",
41352         "cl",
41353         "56"
41354       ],
41355       [
41356         "China (中国)",
41357         "cn",
41358         "86"
41359       ],
41360       [
41361         "Christmas Island",
41362         "cx",
41363         "61",
41364         2
41365       ],
41366       [
41367         "Cocos (Keeling) Islands",
41368         "cc",
41369         "61",
41370         1
41371       ],
41372       [
41373         "Colombia",
41374         "co",
41375         "57"
41376       ],
41377       [
41378         "Comoros (‫جزر القمر‬‎)",
41379         "km",
41380         "269"
41381       ],
41382       [
41383         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41384         "cd",
41385         "243"
41386       ],
41387       [
41388         "Congo (Republic) (Congo-Brazzaville)",
41389         "cg",
41390         "242"
41391       ],
41392       [
41393         "Cook Islands",
41394         "ck",
41395         "682"
41396       ],
41397       [
41398         "Costa Rica",
41399         "cr",
41400         "506"
41401       ],
41402       [
41403         "Côte d’Ivoire",
41404         "ci",
41405         "225"
41406       ],
41407       [
41408         "Croatia (Hrvatska)",
41409         "hr",
41410         "385"
41411       ],
41412       [
41413         "Cuba",
41414         "cu",
41415         "53"
41416       ],
41417       [
41418         "Curaçao",
41419         "cw",
41420         "599",
41421         0
41422       ],
41423       [
41424         "Cyprus (Κύπρος)",
41425         "cy",
41426         "357"
41427       ],
41428       [
41429         "Czech Republic (Česká republika)",
41430         "cz",
41431         "420"
41432       ],
41433       [
41434         "Denmark (Danmark)",
41435         "dk",
41436         "45"
41437       ],
41438       [
41439         "Djibouti",
41440         "dj",
41441         "253"
41442       ],
41443       [
41444         "Dominica",
41445         "dm",
41446         "1767"
41447       ],
41448       [
41449         "Dominican Republic (República Dominicana)",
41450         "do",
41451         "1",
41452         2,
41453         ["809", "829", "849"]
41454       ],
41455       [
41456         "Ecuador",
41457         "ec",
41458         "593"
41459       ],
41460       [
41461         "Egypt (‫مصر‬‎)",
41462         "eg",
41463         "20"
41464       ],
41465       [
41466         "El Salvador",
41467         "sv",
41468         "503"
41469       ],
41470       [
41471         "Equatorial Guinea (Guinea Ecuatorial)",
41472         "gq",
41473         "240"
41474       ],
41475       [
41476         "Eritrea",
41477         "er",
41478         "291"
41479       ],
41480       [
41481         "Estonia (Eesti)",
41482         "ee",
41483         "372"
41484       ],
41485       [
41486         "Ethiopia",
41487         "et",
41488         "251"
41489       ],
41490       [
41491         "Falkland Islands (Islas Malvinas)",
41492         "fk",
41493         "500"
41494       ],
41495       [
41496         "Faroe Islands (Føroyar)",
41497         "fo",
41498         "298"
41499       ],
41500       [
41501         "Fiji",
41502         "fj",
41503         "679"
41504       ],
41505       [
41506         "Finland (Suomi)",
41507         "fi",
41508         "358",
41509         0
41510       ],
41511       [
41512         "France",
41513         "fr",
41514         "33"
41515       ],
41516       [
41517         "French Guiana (Guyane française)",
41518         "gf",
41519         "594"
41520       ],
41521       [
41522         "French Polynesia (Polynésie française)",
41523         "pf",
41524         "689"
41525       ],
41526       [
41527         "Gabon",
41528         "ga",
41529         "241"
41530       ],
41531       [
41532         "Gambia",
41533         "gm",
41534         "220"
41535       ],
41536       [
41537         "Georgia (საქართველო)",
41538         "ge",
41539         "995"
41540       ],
41541       [
41542         "Germany (Deutschland)",
41543         "de",
41544         "49"
41545       ],
41546       [
41547         "Ghana (Gaana)",
41548         "gh",
41549         "233"
41550       ],
41551       [
41552         "Gibraltar",
41553         "gi",
41554         "350"
41555       ],
41556       [
41557         "Greece (Ελλάδα)",
41558         "gr",
41559         "30"
41560       ],
41561       [
41562         "Greenland (Kalaallit Nunaat)",
41563         "gl",
41564         "299"
41565       ],
41566       [
41567         "Grenada",
41568         "gd",
41569         "1473"
41570       ],
41571       [
41572         "Guadeloupe",
41573         "gp",
41574         "590",
41575         0
41576       ],
41577       [
41578         "Guam",
41579         "gu",
41580         "1671"
41581       ],
41582       [
41583         "Guatemala",
41584         "gt",
41585         "502"
41586       ],
41587       [
41588         "Guernsey",
41589         "gg",
41590         "44",
41591         1
41592       ],
41593       [
41594         "Guinea (Guinée)",
41595         "gn",
41596         "224"
41597       ],
41598       [
41599         "Guinea-Bissau (Guiné Bissau)",
41600         "gw",
41601         "245"
41602       ],
41603       [
41604         "Guyana",
41605         "gy",
41606         "592"
41607       ],
41608       [
41609         "Haiti",
41610         "ht",
41611         "509"
41612       ],
41613       [
41614         "Honduras",
41615         "hn",
41616         "504"
41617       ],
41618       [
41619         "Hong Kong (香港)",
41620         "hk",
41621         "852"
41622       ],
41623       [
41624         "Hungary (Magyarország)",
41625         "hu",
41626         "36"
41627       ],
41628       [
41629         "Iceland (Ísland)",
41630         "is",
41631         "354"
41632       ],
41633       [
41634         "India (भारत)",
41635         "in",
41636         "91"
41637       ],
41638       [
41639         "Indonesia",
41640         "id",
41641         "62"
41642       ],
41643       [
41644         "Iran (‫ایران‬‎)",
41645         "ir",
41646         "98"
41647       ],
41648       [
41649         "Iraq (‫العراق‬‎)",
41650         "iq",
41651         "964"
41652       ],
41653       [
41654         "Ireland",
41655         "ie",
41656         "353"
41657       ],
41658       [
41659         "Isle of Man",
41660         "im",
41661         "44",
41662         2
41663       ],
41664       [
41665         "Israel (‫ישראל‬‎)",
41666         "il",
41667         "972"
41668       ],
41669       [
41670         "Italy (Italia)",
41671         "it",
41672         "39",
41673         0
41674       ],
41675       [
41676         "Jamaica",
41677         "jm",
41678         "1876"
41679       ],
41680       [
41681         "Japan (日本)",
41682         "jp",
41683         "81"
41684       ],
41685       [
41686         "Jersey",
41687         "je",
41688         "44",
41689         3
41690       ],
41691       [
41692         "Jordan (‫الأردن‬‎)",
41693         "jo",
41694         "962"
41695       ],
41696       [
41697         "Kazakhstan (Казахстан)",
41698         "kz",
41699         "7",
41700         1
41701       ],
41702       [
41703         "Kenya",
41704         "ke",
41705         "254"
41706       ],
41707       [
41708         "Kiribati",
41709         "ki",
41710         "686"
41711       ],
41712       [
41713         "Kosovo",
41714         "xk",
41715         "383"
41716       ],
41717       [
41718         "Kuwait (‫الكويت‬‎)",
41719         "kw",
41720         "965"
41721       ],
41722       [
41723         "Kyrgyzstan (Кыргызстан)",
41724         "kg",
41725         "996"
41726       ],
41727       [
41728         "Laos (ລາວ)",
41729         "la",
41730         "856"
41731       ],
41732       [
41733         "Latvia (Latvija)",
41734         "lv",
41735         "371"
41736       ],
41737       [
41738         "Lebanon (‫لبنان‬‎)",
41739         "lb",
41740         "961"
41741       ],
41742       [
41743         "Lesotho",
41744         "ls",
41745         "266"
41746       ],
41747       [
41748         "Liberia",
41749         "lr",
41750         "231"
41751       ],
41752       [
41753         "Libya (‫ليبيا‬‎)",
41754         "ly",
41755         "218"
41756       ],
41757       [
41758         "Liechtenstein",
41759         "li",
41760         "423"
41761       ],
41762       [
41763         "Lithuania (Lietuva)",
41764         "lt",
41765         "370"
41766       ],
41767       [
41768         "Luxembourg",
41769         "lu",
41770         "352"
41771       ],
41772       [
41773         "Macau (澳門)",
41774         "mo",
41775         "853"
41776       ],
41777       [
41778         "Macedonia (FYROM) (Македонија)",
41779         "mk",
41780         "389"
41781       ],
41782       [
41783         "Madagascar (Madagasikara)",
41784         "mg",
41785         "261"
41786       ],
41787       [
41788         "Malawi",
41789         "mw",
41790         "265"
41791       ],
41792       [
41793         "Malaysia",
41794         "my",
41795         "60"
41796       ],
41797       [
41798         "Maldives",
41799         "mv",
41800         "960"
41801       ],
41802       [
41803         "Mali",
41804         "ml",
41805         "223"
41806       ],
41807       [
41808         "Malta",
41809         "mt",
41810         "356"
41811       ],
41812       [
41813         "Marshall Islands",
41814         "mh",
41815         "692"
41816       ],
41817       [
41818         "Martinique",
41819         "mq",
41820         "596"
41821       ],
41822       [
41823         "Mauritania (‫موريتانيا‬‎)",
41824         "mr",
41825         "222"
41826       ],
41827       [
41828         "Mauritius (Moris)",
41829         "mu",
41830         "230"
41831       ],
41832       [
41833         "Mayotte",
41834         "yt",
41835         "262",
41836         1
41837       ],
41838       [
41839         "Mexico (México)",
41840         "mx",
41841         "52"
41842       ],
41843       [
41844         "Micronesia",
41845         "fm",
41846         "691"
41847       ],
41848       [
41849         "Moldova (Republica Moldova)",
41850         "md",
41851         "373"
41852       ],
41853       [
41854         "Monaco",
41855         "mc",
41856         "377"
41857       ],
41858       [
41859         "Mongolia (Монгол)",
41860         "mn",
41861         "976"
41862       ],
41863       [
41864         "Montenegro (Crna Gora)",
41865         "me",
41866         "382"
41867       ],
41868       [
41869         "Montserrat",
41870         "ms",
41871         "1664"
41872       ],
41873       [
41874         "Morocco (‫المغرب‬‎)",
41875         "ma",
41876         "212",
41877         0
41878       ],
41879       [
41880         "Mozambique (Moçambique)",
41881         "mz",
41882         "258"
41883       ],
41884       [
41885         "Myanmar (Burma) (မြန်မာ)",
41886         "mm",
41887         "95"
41888       ],
41889       [
41890         "Namibia (Namibië)",
41891         "na",
41892         "264"
41893       ],
41894       [
41895         "Nauru",
41896         "nr",
41897         "674"
41898       ],
41899       [
41900         "Nepal (नेपाल)",
41901         "np",
41902         "977"
41903       ],
41904       [
41905         "Netherlands (Nederland)",
41906         "nl",
41907         "31"
41908       ],
41909       [
41910         "New Caledonia (Nouvelle-Calédonie)",
41911         "nc",
41912         "687"
41913       ],
41914       [
41915         "New Zealand",
41916         "nz",
41917         "64"
41918       ],
41919       [
41920         "Nicaragua",
41921         "ni",
41922         "505"
41923       ],
41924       [
41925         "Niger (Nijar)",
41926         "ne",
41927         "227"
41928       ],
41929       [
41930         "Nigeria",
41931         "ng",
41932         "234"
41933       ],
41934       [
41935         "Niue",
41936         "nu",
41937         "683"
41938       ],
41939       [
41940         "Norfolk Island",
41941         "nf",
41942         "672"
41943       ],
41944       [
41945         "North Korea (조선 민주주의 인민 공화국)",
41946         "kp",
41947         "850"
41948       ],
41949       [
41950         "Northern Mariana Islands",
41951         "mp",
41952         "1670"
41953       ],
41954       [
41955         "Norway (Norge)",
41956         "no",
41957         "47",
41958         0
41959       ],
41960       [
41961         "Oman (‫عُمان‬‎)",
41962         "om",
41963         "968"
41964       ],
41965       [
41966         "Pakistan (‫پاکستان‬‎)",
41967         "pk",
41968         "92"
41969       ],
41970       [
41971         "Palau",
41972         "pw",
41973         "680"
41974       ],
41975       [
41976         "Palestine (‫فلسطين‬‎)",
41977         "ps",
41978         "970"
41979       ],
41980       [
41981         "Panama (Panamá)",
41982         "pa",
41983         "507"
41984       ],
41985       [
41986         "Papua New Guinea",
41987         "pg",
41988         "675"
41989       ],
41990       [
41991         "Paraguay",
41992         "py",
41993         "595"
41994       ],
41995       [
41996         "Peru (Perú)",
41997         "pe",
41998         "51"
41999       ],
42000       [
42001         "Philippines",
42002         "ph",
42003         "63"
42004       ],
42005       [
42006         "Poland (Polska)",
42007         "pl",
42008         "48"
42009       ],
42010       [
42011         "Portugal",
42012         "pt",
42013         "351"
42014       ],
42015       [
42016         "Puerto Rico",
42017         "pr",
42018         "1",
42019         3,
42020         ["787", "939"]
42021       ],
42022       [
42023         "Qatar (‫قطر‬‎)",
42024         "qa",
42025         "974"
42026       ],
42027       [
42028         "Réunion (La Réunion)",
42029         "re",
42030         "262",
42031         0
42032       ],
42033       [
42034         "Romania (România)",
42035         "ro",
42036         "40"
42037       ],
42038       [
42039         "Russia (Россия)",
42040         "ru",
42041         "7",
42042         0
42043       ],
42044       [
42045         "Rwanda",
42046         "rw",
42047         "250"
42048       ],
42049       [
42050         "Saint Barthélemy",
42051         "bl",
42052         "590",
42053         1
42054       ],
42055       [
42056         "Saint Helena",
42057         "sh",
42058         "290"
42059       ],
42060       [
42061         "Saint Kitts and Nevis",
42062         "kn",
42063         "1869"
42064       ],
42065       [
42066         "Saint Lucia",
42067         "lc",
42068         "1758"
42069       ],
42070       [
42071         "Saint Martin (Saint-Martin (partie française))",
42072         "mf",
42073         "590",
42074         2
42075       ],
42076       [
42077         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42078         "pm",
42079         "508"
42080       ],
42081       [
42082         "Saint Vincent and the Grenadines",
42083         "vc",
42084         "1784"
42085       ],
42086       [
42087         "Samoa",
42088         "ws",
42089         "685"
42090       ],
42091       [
42092         "San Marino",
42093         "sm",
42094         "378"
42095       ],
42096       [
42097         "São Tomé and Príncipe (São Tomé e Príncipe)",
42098         "st",
42099         "239"
42100       ],
42101       [
42102         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42103         "sa",
42104         "966"
42105       ],
42106       [
42107         "Senegal (Sénégal)",
42108         "sn",
42109         "221"
42110       ],
42111       [
42112         "Serbia (Србија)",
42113         "rs",
42114         "381"
42115       ],
42116       [
42117         "Seychelles",
42118         "sc",
42119         "248"
42120       ],
42121       [
42122         "Sierra Leone",
42123         "sl",
42124         "232"
42125       ],
42126       [
42127         "Singapore",
42128         "sg",
42129         "65"
42130       ],
42131       [
42132         "Sint Maarten",
42133         "sx",
42134         "1721"
42135       ],
42136       [
42137         "Slovakia (Slovensko)",
42138         "sk",
42139         "421"
42140       ],
42141       [
42142         "Slovenia (Slovenija)",
42143         "si",
42144         "386"
42145       ],
42146       [
42147         "Solomon Islands",
42148         "sb",
42149         "677"
42150       ],
42151       [
42152         "Somalia (Soomaaliya)",
42153         "so",
42154         "252"
42155       ],
42156       [
42157         "South Africa",
42158         "za",
42159         "27"
42160       ],
42161       [
42162         "South Korea (대한민국)",
42163         "kr",
42164         "82"
42165       ],
42166       [
42167         "South Sudan (‫جنوب السودان‬‎)",
42168         "ss",
42169         "211"
42170       ],
42171       [
42172         "Spain (España)",
42173         "es",
42174         "34"
42175       ],
42176       [
42177         "Sri Lanka (ශ්‍රී ලංකාව)",
42178         "lk",
42179         "94"
42180       ],
42181       [
42182         "Sudan (‫السودان‬‎)",
42183         "sd",
42184         "249"
42185       ],
42186       [
42187         "Suriname",
42188         "sr",
42189         "597"
42190       ],
42191       [
42192         "Svalbard and Jan Mayen",
42193         "sj",
42194         "47",
42195         1
42196       ],
42197       [
42198         "Swaziland",
42199         "sz",
42200         "268"
42201       ],
42202       [
42203         "Sweden (Sverige)",
42204         "se",
42205         "46"
42206       ],
42207       [
42208         "Switzerland (Schweiz)",
42209         "ch",
42210         "41"
42211       ],
42212       [
42213         "Syria (‫سوريا‬‎)",
42214         "sy",
42215         "963"
42216       ],
42217       [
42218         "Taiwan (台灣)",
42219         "tw",
42220         "886"
42221       ],
42222       [
42223         "Tajikistan",
42224         "tj",
42225         "992"
42226       ],
42227       [
42228         "Tanzania",
42229         "tz",
42230         "255"
42231       ],
42232       [
42233         "Thailand (ไทย)",
42234         "th",
42235         "66"
42236       ],
42237       [
42238         "Timor-Leste",
42239         "tl",
42240         "670"
42241       ],
42242       [
42243         "Togo",
42244         "tg",
42245         "228"
42246       ],
42247       [
42248         "Tokelau",
42249         "tk",
42250         "690"
42251       ],
42252       [
42253         "Tonga",
42254         "to",
42255         "676"
42256       ],
42257       [
42258         "Trinidad and Tobago",
42259         "tt",
42260         "1868"
42261       ],
42262       [
42263         "Tunisia (‫تونس‬‎)",
42264         "tn",
42265         "216"
42266       ],
42267       [
42268         "Turkey (Türkiye)",
42269         "tr",
42270         "90"
42271       ],
42272       [
42273         "Turkmenistan",
42274         "tm",
42275         "993"
42276       ],
42277       [
42278         "Turks and Caicos Islands",
42279         "tc",
42280         "1649"
42281       ],
42282       [
42283         "Tuvalu",
42284         "tv",
42285         "688"
42286       ],
42287       [
42288         "U.S. Virgin Islands",
42289         "vi",
42290         "1340"
42291       ],
42292       [
42293         "Uganda",
42294         "ug",
42295         "256"
42296       ],
42297       [
42298         "Ukraine (Україна)",
42299         "ua",
42300         "380"
42301       ],
42302       [
42303         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42304         "ae",
42305         "971"
42306       ],
42307       [
42308         "United Kingdom",
42309         "gb",
42310         "44",
42311         0
42312       ],
42313       [
42314         "United States",
42315         "us",
42316         "1",
42317         0
42318       ],
42319       [
42320         "Uruguay",
42321         "uy",
42322         "598"
42323       ],
42324       [
42325         "Uzbekistan (Oʻzbekiston)",
42326         "uz",
42327         "998"
42328       ],
42329       [
42330         "Vanuatu",
42331         "vu",
42332         "678"
42333       ],
42334       [
42335         "Vatican City (Città del Vaticano)",
42336         "va",
42337         "39",
42338         1
42339       ],
42340       [
42341         "Venezuela",
42342         "ve",
42343         "58"
42344       ],
42345       [
42346         "Vietnam (Việt Nam)",
42347         "vn",
42348         "84"
42349       ],
42350       [
42351         "Wallis and Futuna (Wallis-et-Futuna)",
42352         "wf",
42353         "681"
42354       ],
42355       [
42356         "Western Sahara (‫الصحراء الغربية‬‎)",
42357         "eh",
42358         "212",
42359         1
42360       ],
42361       [
42362         "Yemen (‫اليمن‬‎)",
42363         "ye",
42364         "967"
42365       ],
42366       [
42367         "Zambia",
42368         "zm",
42369         "260"
42370       ],
42371       [
42372         "Zimbabwe",
42373         "zw",
42374         "263"
42375       ],
42376       [
42377         "Åland Islands",
42378         "ax",
42379         "358",
42380         1
42381       ]
42382   ];
42383   
42384   return d;
42385 }/**
42386 *    This script refer to:
42387 *    Title: International Telephone Input
42388 *    Author: Jack O'Connor
42389 *    Code version:  v12.1.12
42390 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42391 **/
42392
42393 /**
42394  * @class Roo.bootstrap.PhoneInput
42395  * @extends Roo.bootstrap.TriggerField
42396  * An input with International dial-code selection
42397  
42398  * @cfg {String} defaultDialCode default '+852'
42399  * @cfg {Array} preferedCountries default []
42400   
42401  * @constructor
42402  * Create a new PhoneInput.
42403  * @param {Object} config Configuration options
42404  */
42405
42406 Roo.bootstrap.PhoneInput = function(config) {
42407     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42408 };
42409
42410 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42411         
42412         listWidth: undefined,
42413         
42414         selectedClass: 'active',
42415         
42416         invalidClass : "has-warning",
42417         
42418         validClass: 'has-success',
42419         
42420         allowed: '0123456789',
42421         
42422         max_length: 15,
42423         
42424         /**
42425          * @cfg {String} defaultDialCode The default dial code when initializing the input
42426          */
42427         defaultDialCode: '+852',
42428         
42429         /**
42430          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
42431          */
42432         preferedCountries: false,
42433         
42434         getAutoCreate : function()
42435         {
42436             var data = Roo.bootstrap.PhoneInputData();
42437             var align = this.labelAlign || this.parentLabelAlign();
42438             var id = Roo.id();
42439             
42440             this.allCountries = [];
42441             this.dialCodeMapping = [];
42442             
42443             for (var i = 0; i < data.length; i++) {
42444               var c = data[i];
42445               this.allCountries[i] = {
42446                 name: c[0],
42447                 iso2: c[1],
42448                 dialCode: c[2],
42449                 priority: c[3] || 0,
42450                 areaCodes: c[4] || null
42451               };
42452               this.dialCodeMapping[c[2]] = {
42453                   name: c[0],
42454                   iso2: c[1],
42455                   priority: c[3] || 0,
42456                   areaCodes: c[4] || null
42457               };
42458             }
42459             
42460             var cfg = {
42461                 cls: 'form-group',
42462                 cn: []
42463             };
42464             
42465             var input =  {
42466                 tag: 'input',
42467                 id : id,
42468                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42469                 maxlength: this.max_length,
42470                 cls : 'form-control tel-input',
42471                 autocomplete: 'new-password'
42472             };
42473             
42474             var hiddenInput = {
42475                 tag: 'input',
42476                 type: 'hidden',
42477                 cls: 'hidden-tel-input'
42478             };
42479             
42480             if (this.name) {
42481                 hiddenInput.name = this.name;
42482             }
42483             
42484             if (this.disabled) {
42485                 input.disabled = true;
42486             }
42487             
42488             var flag_container = {
42489                 tag: 'div',
42490                 cls: 'flag-box',
42491                 cn: [
42492                     {
42493                         tag: 'div',
42494                         cls: 'flag'
42495                     },
42496                     {
42497                         tag: 'div',
42498                         cls: 'caret'
42499                     }
42500                 ]
42501             };
42502             
42503             var box = {
42504                 tag: 'div',
42505                 cls: this.hasFeedback ? 'has-feedback' : '',
42506                 cn: [
42507                     hiddenInput,
42508                     input,
42509                     {
42510                         tag: 'input',
42511                         cls: 'dial-code-holder',
42512                         disabled: true
42513                     }
42514                 ]
42515             };
42516             
42517             var container = {
42518                 cls: 'roo-select2-container input-group',
42519                 cn: [
42520                     flag_container,
42521                     box
42522                 ]
42523             };
42524             
42525             if (this.fieldLabel.length) {
42526                 var indicator = {
42527                     tag: 'i',
42528                     tooltip: 'This field is required'
42529                 };
42530                 
42531                 var label = {
42532                     tag: 'label',
42533                     'for':  id,
42534                     cls: 'control-label',
42535                     cn: []
42536                 };
42537                 
42538                 var label_text = {
42539                     tag: 'span',
42540                     html: this.fieldLabel
42541                 };
42542                 
42543                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42544                 label.cn = [
42545                     indicator,
42546                     label_text
42547                 ];
42548                 
42549                 if(this.indicatorpos == 'right') {
42550                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42551                     label.cn = [
42552                         label_text,
42553                         indicator
42554                     ];
42555                 }
42556                 
42557                 if(align == 'left') {
42558                     container = {
42559                         tag: 'div',
42560                         cn: [
42561                             container
42562                         ]
42563                     };
42564                     
42565                     if(this.labelWidth > 12){
42566                         label.style = "width: " + this.labelWidth + 'px';
42567                     }
42568                     if(this.labelWidth < 13 && this.labelmd == 0){
42569                         this.labelmd = this.labelWidth;
42570                     }
42571                     if(this.labellg > 0){
42572                         label.cls += ' col-lg-' + this.labellg;
42573                         input.cls += ' col-lg-' + (12 - this.labellg);
42574                     }
42575                     if(this.labelmd > 0){
42576                         label.cls += ' col-md-' + this.labelmd;
42577                         container.cls += ' col-md-' + (12 - this.labelmd);
42578                     }
42579                     if(this.labelsm > 0){
42580                         label.cls += ' col-sm-' + this.labelsm;
42581                         container.cls += ' col-sm-' + (12 - this.labelsm);
42582                     }
42583                     if(this.labelxs > 0){
42584                         label.cls += ' col-xs-' + this.labelxs;
42585                         container.cls += ' col-xs-' + (12 - this.labelxs);
42586                     }
42587                 }
42588             }
42589             
42590             cfg.cn = [
42591                 label,
42592                 container
42593             ];
42594             
42595             var settings = this;
42596             
42597             ['xs','sm','md','lg'].map(function(size){
42598                 if (settings[size]) {
42599                     cfg.cls += ' col-' + size + '-' + settings[size];
42600                 }
42601             });
42602             
42603             this.store = new Roo.data.Store({
42604                 proxy : new Roo.data.MemoryProxy({}),
42605                 reader : new Roo.data.JsonReader({
42606                     fields : [
42607                         {
42608                             'name' : 'name',
42609                             'type' : 'string'
42610                         },
42611                         {
42612                             'name' : 'iso2',
42613                             'type' : 'string'
42614                         },
42615                         {
42616                             'name' : 'dialCode',
42617                             'type' : 'string'
42618                         },
42619                         {
42620                             'name' : 'priority',
42621                             'type' : 'string'
42622                         },
42623                         {
42624                             'name' : 'areaCodes',
42625                             'type' : 'string'
42626                         }
42627                     ]
42628                 })
42629             });
42630             
42631             if(!this.preferedCountries) {
42632                 this.preferedCountries = [
42633                     'hk',
42634                     'gb',
42635                     'us'
42636                 ];
42637             }
42638             
42639             var p = this.preferedCountries.reverse();
42640             
42641             if(p) {
42642                 for (var i = 0; i < p.length; i++) {
42643                     for (var j = 0; j < this.allCountries.length; j++) {
42644                         if(this.allCountries[j].iso2 == p[i]) {
42645                             var t = this.allCountries[j];
42646                             this.allCountries.splice(j,1);
42647                             this.allCountries.unshift(t);
42648                         }
42649                     } 
42650                 }
42651             }
42652             
42653             this.store.proxy.data = {
42654                 success: true,
42655                 data: this.allCountries
42656             };
42657             
42658             return cfg;
42659         },
42660         
42661         initEvents : function()
42662         {
42663             this.createList();
42664             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42665             
42666             this.indicator = this.indicatorEl();
42667             this.flag = this.flagEl();
42668             this.dialCodeHolder = this.dialCodeHolderEl();
42669             
42670             this.trigger = this.el.select('div.flag-box',true).first();
42671             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42672             
42673             var _this = this;
42674             
42675             (function(){
42676                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42677                 _this.list.setWidth(lw);
42678             }).defer(100);
42679             
42680             this.list.on('mouseover', this.onViewOver, this);
42681             this.list.on('mousemove', this.onViewMove, this);
42682             this.inputEl().on("keyup", this.onKeyUp, this);
42683             this.inputEl().on("keypress", this.onKeyPress, this);
42684             
42685             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42686
42687             this.view = new Roo.View(this.list, this.tpl, {
42688                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42689             });
42690             
42691             this.view.on('click', this.onViewClick, this);
42692             this.setValue(this.defaultDialCode);
42693         },
42694         
42695         onTriggerClick : function(e)
42696         {
42697             Roo.log('trigger click');
42698             if(this.disabled){
42699                 return;
42700             }
42701             
42702             if(this.isExpanded()){
42703                 this.collapse();
42704                 this.hasFocus = false;
42705             }else {
42706                 this.store.load({});
42707                 this.hasFocus = true;
42708                 this.expand();
42709             }
42710         },
42711         
42712         isExpanded : function()
42713         {
42714             return this.list.isVisible();
42715         },
42716         
42717         collapse : function()
42718         {
42719             if(!this.isExpanded()){
42720                 return;
42721             }
42722             this.list.hide();
42723             Roo.get(document).un('mousedown', this.collapseIf, this);
42724             Roo.get(document).un('mousewheel', this.collapseIf, this);
42725             this.fireEvent('collapse', this);
42726             this.validate();
42727         },
42728         
42729         expand : function()
42730         {
42731             Roo.log('expand');
42732
42733             if(this.isExpanded() || !this.hasFocus){
42734                 return;
42735             }
42736             
42737             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42738             this.list.setWidth(lw);
42739             
42740             this.list.show();
42741             this.restrictHeight();
42742             
42743             Roo.get(document).on('mousedown', this.collapseIf, this);
42744             Roo.get(document).on('mousewheel', this.collapseIf, this);
42745             
42746             this.fireEvent('expand', this);
42747         },
42748         
42749         restrictHeight : function()
42750         {
42751             this.list.alignTo(this.inputEl(), this.listAlign);
42752             this.list.alignTo(this.inputEl(), this.listAlign);
42753         },
42754         
42755         onViewOver : function(e, t)
42756         {
42757             if(this.inKeyMode){
42758                 return;
42759             }
42760             var item = this.view.findItemFromChild(t);
42761             
42762             if(item){
42763                 var index = this.view.indexOf(item);
42764                 this.select(index, false);
42765             }
42766         },
42767
42768         // private
42769         onViewClick : function(view, doFocus, el, e)
42770         {
42771             var index = this.view.getSelectedIndexes()[0];
42772             
42773             var r = this.store.getAt(index);
42774             
42775             if(r){
42776                 this.onSelect(r, index);
42777             }
42778             if(doFocus !== false && !this.blockFocus){
42779                 this.inputEl().focus();
42780             }
42781         },
42782         
42783         onViewMove : function(e, t)
42784         {
42785             this.inKeyMode = false;
42786         },
42787         
42788         select : function(index, scrollIntoView)
42789         {
42790             this.selectedIndex = index;
42791             this.view.select(index);
42792             if(scrollIntoView !== false){
42793                 var el = this.view.getNode(index);
42794                 if(el){
42795                     this.list.scrollChildIntoView(el, false);
42796                 }
42797             }
42798         },
42799         
42800         createList : function()
42801         {
42802             this.list = Roo.get(document.body).createChild({
42803                 tag: 'ul',
42804                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42805                 style: 'display:none'
42806             });
42807             
42808             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42809         },
42810         
42811         collapseIf : function(e)
42812         {
42813             var in_combo  = e.within(this.el);
42814             var in_list =  e.within(this.list);
42815             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42816             
42817             if (in_combo || in_list || is_list) {
42818                 return;
42819             }
42820             this.collapse();
42821         },
42822         
42823         onSelect : function(record, index)
42824         {
42825             if(this.fireEvent('beforeselect', this, record, index) !== false){
42826                 
42827                 this.setFlagClass(record.data.iso2);
42828                 this.setDialCode(record.data.dialCode);
42829                 this.hasFocus = false;
42830                 this.collapse();
42831                 this.fireEvent('select', this, record, index);
42832             }
42833         },
42834         
42835         flagEl : function()
42836         {
42837             var flag = this.el.select('div.flag',true).first();
42838             if(!flag){
42839                 return false;
42840             }
42841             return flag;
42842         },
42843         
42844         dialCodeHolderEl : function()
42845         {
42846             var d = this.el.select('input.dial-code-holder',true).first();
42847             if(!d){
42848                 return false;
42849             }
42850             return d;
42851         },
42852         
42853         setDialCode : function(v)
42854         {
42855             this.dialCodeHolder.dom.value = '+'+v;
42856         },
42857         
42858         setFlagClass : function(n)
42859         {
42860             this.flag.dom.className = 'flag '+n;
42861         },
42862         
42863         getValue : function()
42864         {
42865             var v = this.inputEl().getValue();
42866             if(this.dialCodeHolder) {
42867                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42868             }
42869             return v;
42870         },
42871         
42872         setValue : function(v)
42873         {
42874             var d = this.getDialCode(v);
42875             
42876             //invalid dial code
42877             if(v.length == 0 || !d || d.length == 0) {
42878                 if(this.rendered){
42879                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42880                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42881                 }
42882                 return;
42883             }
42884             
42885             //valid dial code
42886             this.setFlagClass(this.dialCodeMapping[d].iso2);
42887             this.setDialCode(d);
42888             this.inputEl().dom.value = v.replace('+'+d,'');
42889             this.hiddenEl().dom.value = this.getValue();
42890             
42891             this.validate();
42892         },
42893         
42894         getDialCode : function(v)
42895         {
42896             v = v ||  '';
42897             
42898             if (v.length == 0) {
42899                 return this.dialCodeHolder.dom.value;
42900             }
42901             
42902             var dialCode = "";
42903             if (v.charAt(0) != "+") {
42904                 return false;
42905             }
42906             var numericChars = "";
42907             for (var i = 1; i < v.length; i++) {
42908               var c = v.charAt(i);
42909               if (!isNaN(c)) {
42910                 numericChars += c;
42911                 if (this.dialCodeMapping[numericChars]) {
42912                   dialCode = v.substr(1, i);
42913                 }
42914                 if (numericChars.length == 4) {
42915                   break;
42916                 }
42917               }
42918             }
42919             return dialCode;
42920         },
42921         
42922         reset : function()
42923         {
42924             this.setValue(this.defaultDialCode);
42925             this.validate();
42926         },
42927         
42928         hiddenEl : function()
42929         {
42930             return this.el.select('input.hidden-tel-input',true).first();
42931         },
42932         
42933         // after setting val
42934         onKeyUp : function(e){
42935             this.setValue(this.getValue());
42936         },
42937         
42938         onKeyPress : function(e){
42939             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42940                 e.stopEvent();
42941             }
42942         }
42943         
42944 });
42945 /**
42946  * @class Roo.bootstrap.MoneyField
42947  * @extends Roo.bootstrap.ComboBox
42948  * Bootstrap MoneyField class
42949  * 
42950  * @constructor
42951  * Create a new MoneyField.
42952  * @param {Object} config Configuration options
42953  */
42954
42955 Roo.bootstrap.MoneyField = function(config) {
42956     
42957     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42958     
42959 };
42960
42961 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42962     
42963     /**
42964      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42965      */
42966     allowDecimals : true,
42967     /**
42968      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42969      */
42970     decimalSeparator : ".",
42971     /**
42972      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42973      */
42974     decimalPrecision : 0,
42975     /**
42976      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42977      */
42978     allowNegative : true,
42979     /**
42980      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42981      */
42982     allowZero: true,
42983     /**
42984      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42985      */
42986     minValue : Number.NEGATIVE_INFINITY,
42987     /**
42988      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42989      */
42990     maxValue : Number.MAX_VALUE,
42991     /**
42992      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42993      */
42994     minText : "The minimum value for this field is {0}",
42995     /**
42996      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42997      */
42998     maxText : "The maximum value for this field is {0}",
42999     /**
43000      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43001      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43002      */
43003     nanText : "{0} is not a valid number",
43004     /**
43005      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43006      */
43007     castInt : true,
43008     /**
43009      * @cfg {String} defaults currency of the MoneyField
43010      * value should be in lkey
43011      */
43012     defaultCurrency : false,
43013     /**
43014      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43015      */
43016     thousandsDelimiter : false,
43017     /**
43018      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43019      */
43020     max_length: false,
43021     
43022     inputlg : 9,
43023     inputmd : 9,
43024     inputsm : 9,
43025     inputxs : 6,
43026     
43027     store : false,
43028     
43029     getAutoCreate : function()
43030     {
43031         var align = this.labelAlign || this.parentLabelAlign();
43032         
43033         var id = Roo.id();
43034
43035         var cfg = {
43036             cls: 'form-group',
43037             cn: []
43038         };
43039
43040         var input =  {
43041             tag: 'input',
43042             id : id,
43043             cls : 'form-control roo-money-amount-input',
43044             autocomplete: 'new-password'
43045         };
43046         
43047         var hiddenInput = {
43048             tag: 'input',
43049             type: 'hidden',
43050             id: Roo.id(),
43051             cls: 'hidden-number-input'
43052         };
43053         
43054         if(this.max_length) {
43055             input.maxlength = this.max_length; 
43056         }
43057         
43058         if (this.name) {
43059             hiddenInput.name = this.name;
43060         }
43061
43062         if (this.disabled) {
43063             input.disabled = true;
43064         }
43065
43066         var clg = 12 - this.inputlg;
43067         var cmd = 12 - this.inputmd;
43068         var csm = 12 - this.inputsm;
43069         var cxs = 12 - this.inputxs;
43070         
43071         var container = {
43072             tag : 'div',
43073             cls : 'row roo-money-field',
43074             cn : [
43075                 {
43076                     tag : 'div',
43077                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43078                     cn : [
43079                         {
43080                             tag : 'div',
43081                             cls: 'roo-select2-container input-group',
43082                             cn: [
43083                                 {
43084                                     tag : 'input',
43085                                     cls : 'form-control roo-money-currency-input',
43086                                     autocomplete: 'new-password',
43087                                     readOnly : 1,
43088                                     name : this.currencyName
43089                                 },
43090                                 {
43091                                     tag :'span',
43092                                     cls : 'input-group-addon',
43093                                     cn : [
43094                                         {
43095                                             tag: 'span',
43096                                             cls: 'caret'
43097                                         }
43098                                     ]
43099                                 }
43100                             ]
43101                         }
43102                     ]
43103                 },
43104                 {
43105                     tag : 'div',
43106                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43107                     cn : [
43108                         {
43109                             tag: 'div',
43110                             cls: this.hasFeedback ? 'has-feedback' : '',
43111                             cn: [
43112                                 input
43113                             ]
43114                         }
43115                     ]
43116                 }
43117             ]
43118             
43119         };
43120         
43121         if (this.fieldLabel.length) {
43122             var indicator = {
43123                 tag: 'i',
43124                 tooltip: 'This field is required'
43125             };
43126
43127             var label = {
43128                 tag: 'label',
43129                 'for':  id,
43130                 cls: 'control-label',
43131                 cn: []
43132             };
43133
43134             var label_text = {
43135                 tag: 'span',
43136                 html: this.fieldLabel
43137             };
43138
43139             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43140             label.cn = [
43141                 indicator,
43142                 label_text
43143             ];
43144
43145             if(this.indicatorpos == 'right') {
43146                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43147                 label.cn = [
43148                     label_text,
43149                     indicator
43150                 ];
43151             }
43152
43153             if(align == 'left') {
43154                 container = {
43155                     tag: 'div',
43156                     cn: [
43157                         container
43158                     ]
43159                 };
43160
43161                 if(this.labelWidth > 12){
43162                     label.style = "width: " + this.labelWidth + 'px';
43163                 }
43164                 if(this.labelWidth < 13 && this.labelmd == 0){
43165                     this.labelmd = this.labelWidth;
43166                 }
43167                 if(this.labellg > 0){
43168                     label.cls += ' col-lg-' + this.labellg;
43169                     input.cls += ' col-lg-' + (12 - this.labellg);
43170                 }
43171                 if(this.labelmd > 0){
43172                     label.cls += ' col-md-' + this.labelmd;
43173                     container.cls += ' col-md-' + (12 - this.labelmd);
43174                 }
43175                 if(this.labelsm > 0){
43176                     label.cls += ' col-sm-' + this.labelsm;
43177                     container.cls += ' col-sm-' + (12 - this.labelsm);
43178                 }
43179                 if(this.labelxs > 0){
43180                     label.cls += ' col-xs-' + this.labelxs;
43181                     container.cls += ' col-xs-' + (12 - this.labelxs);
43182                 }
43183             }
43184         }
43185
43186         cfg.cn = [
43187             label,
43188             container,
43189             hiddenInput
43190         ];
43191         
43192         var settings = this;
43193
43194         ['xs','sm','md','lg'].map(function(size){
43195             if (settings[size]) {
43196                 cfg.cls += ' col-' + size + '-' + settings[size];
43197             }
43198         });
43199         
43200         return cfg;
43201     },
43202     
43203     initEvents : function()
43204     {
43205         this.indicator = this.indicatorEl();
43206         
43207         this.initCurrencyEvent();
43208         
43209         this.initNumberEvent();
43210     },
43211     
43212     initCurrencyEvent : function()
43213     {
43214         if (!this.store) {
43215             throw "can not find store for combo";
43216         }
43217         
43218         this.store = Roo.factory(this.store, Roo.data);
43219         this.store.parent = this;
43220         
43221         this.createList();
43222         
43223         this.triggerEl = this.el.select('.input-group-addon', true).first();
43224         
43225         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43226         
43227         var _this = this;
43228         
43229         (function(){
43230             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43231             _this.list.setWidth(lw);
43232         }).defer(100);
43233         
43234         this.list.on('mouseover', this.onViewOver, this);
43235         this.list.on('mousemove', this.onViewMove, this);
43236         this.list.on('scroll', this.onViewScroll, this);
43237         
43238         if(!this.tpl){
43239             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43240         }
43241         
43242         this.view = new Roo.View(this.list, this.tpl, {
43243             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43244         });
43245         
43246         this.view.on('click', this.onViewClick, this);
43247         
43248         this.store.on('beforeload', this.onBeforeLoad, this);
43249         this.store.on('load', this.onLoad, this);
43250         this.store.on('loadexception', this.onLoadException, this);
43251         
43252         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43253             "up" : function(e){
43254                 this.inKeyMode = true;
43255                 this.selectPrev();
43256             },
43257
43258             "down" : function(e){
43259                 if(!this.isExpanded()){
43260                     this.onTriggerClick();
43261                 }else{
43262                     this.inKeyMode = true;
43263                     this.selectNext();
43264                 }
43265             },
43266
43267             "enter" : function(e){
43268                 this.collapse();
43269                 
43270                 if(this.fireEvent("specialkey", this, e)){
43271                     this.onViewClick(false);
43272                 }
43273                 
43274                 return true;
43275             },
43276
43277             "esc" : function(e){
43278                 this.collapse();
43279             },
43280
43281             "tab" : function(e){
43282                 this.collapse();
43283                 
43284                 if(this.fireEvent("specialkey", this, e)){
43285                     this.onViewClick(false);
43286                 }
43287                 
43288                 return true;
43289             },
43290
43291             scope : this,
43292
43293             doRelay : function(foo, bar, hname){
43294                 if(hname == 'down' || this.scope.isExpanded()){
43295                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43296                 }
43297                 return true;
43298             },
43299
43300             forceKeyDown: true
43301         });
43302         
43303         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43304         
43305     },
43306     
43307     initNumberEvent : function(e)
43308     {
43309         this.inputEl().on("keydown" , this.fireKey,  this);
43310         this.inputEl().on("focus", this.onFocus,  this);
43311         this.inputEl().on("blur", this.onBlur,  this);
43312         
43313         this.inputEl().relayEvent('keyup', this);
43314         
43315         if(this.indicator){
43316             this.indicator.addClass('invisible');
43317         }
43318  
43319         this.originalValue = this.getValue();
43320         
43321         if(this.validationEvent == 'keyup'){
43322             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43323             this.inputEl().on('keyup', this.filterValidation, this);
43324         }
43325         else if(this.validationEvent !== false){
43326             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43327         }
43328         
43329         if(this.selectOnFocus){
43330             this.on("focus", this.preFocus, this);
43331             
43332         }
43333         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43334             this.inputEl().on("keypress", this.filterKeys, this);
43335         } else {
43336             this.inputEl().relayEvent('keypress', this);
43337         }
43338         
43339         var allowed = "0123456789";
43340         
43341         if(this.allowDecimals){
43342             allowed += this.decimalSeparator;
43343         }
43344         
43345         if(this.allowNegative){
43346             allowed += "-";
43347         }
43348         
43349         if(this.thousandsDelimiter) {
43350             allowed += ",";
43351         }
43352         
43353         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43354         
43355         var keyPress = function(e){
43356             
43357             var k = e.getKey();
43358             
43359             var c = e.getCharCode();
43360             
43361             if(
43362                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43363                     allowed.indexOf(String.fromCharCode(c)) === -1
43364             ){
43365                 e.stopEvent();
43366                 return;
43367             }
43368             
43369             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43370                 return;
43371             }
43372             
43373             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43374                 e.stopEvent();
43375             }
43376         };
43377         
43378         this.inputEl().on("keypress", keyPress, this);
43379         
43380     },
43381     
43382     onTriggerClick : function(e)
43383     {   
43384         if(this.disabled){
43385             return;
43386         }
43387         
43388         this.page = 0;
43389         this.loadNext = false;
43390         
43391         if(this.isExpanded()){
43392             this.collapse();
43393             return;
43394         }
43395         
43396         this.hasFocus = true;
43397         
43398         if(this.triggerAction == 'all') {
43399             this.doQuery(this.allQuery, true);
43400             return;
43401         }
43402         
43403         this.doQuery(this.getRawValue());
43404     },
43405     
43406     getCurrency : function()
43407     {   
43408         var v = this.currencyEl().getValue();
43409         
43410         return v;
43411     },
43412     
43413     restrictHeight : function()
43414     {
43415         this.list.alignTo(this.currencyEl(), this.listAlign);
43416         this.list.alignTo(this.currencyEl(), this.listAlign);
43417     },
43418     
43419     onViewClick : function(view, doFocus, el, e)
43420     {
43421         var index = this.view.getSelectedIndexes()[0];
43422         
43423         var r = this.store.getAt(index);
43424         
43425         if(r){
43426             this.onSelect(r, index);
43427         }
43428     },
43429     
43430     onSelect : function(record, index){
43431         
43432         if(this.fireEvent('beforeselect', this, record, index) !== false){
43433         
43434             this.setFromCurrencyData(index > -1 ? record.data : false);
43435             
43436             this.collapse();
43437             
43438             this.fireEvent('select', this, record, index);
43439         }
43440     },
43441     
43442     setFromCurrencyData : function(o)
43443     {
43444         var currency = '';
43445         
43446         this.lastCurrency = o;
43447         
43448         if (this.currencyField) {
43449             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43450         } else {
43451             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43452         }
43453         
43454         this.lastSelectionText = currency;
43455         
43456         //setting default currency
43457         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43458             this.setCurrency(this.defaultCurrency);
43459             return;
43460         }
43461         
43462         this.setCurrency(currency);
43463     },
43464     
43465     setFromData : function(o)
43466     {
43467         var c = {};
43468         
43469         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43470         
43471         this.setFromCurrencyData(c);
43472         
43473         var value = '';
43474         
43475         if (this.name) {
43476             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43477         } else {
43478             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43479         }
43480         
43481         this.setValue(value);
43482         
43483     },
43484     
43485     setCurrency : function(v)
43486     {   
43487         this.currencyValue = v;
43488         
43489         if(this.rendered){
43490             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43491             this.validate();
43492         }
43493     },
43494     
43495     setValue : function(v)
43496     {
43497         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43498         
43499         this.value = v;
43500         
43501         if(this.rendered){
43502             
43503             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43504             
43505             this.inputEl().dom.value = (v == '') ? '' :
43506                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43507             
43508             if(!this.allowZero && v === '0') {
43509                 this.hiddenEl().dom.value = '';
43510                 this.inputEl().dom.value = '';
43511             }
43512             
43513             this.validate();
43514         }
43515     },
43516     
43517     getRawValue : function()
43518     {
43519         var v = this.inputEl().getValue();
43520         
43521         return v;
43522     },
43523     
43524     getValue : function()
43525     {
43526         return this.fixPrecision(this.parseValue(this.getRawValue()));
43527     },
43528     
43529     parseValue : function(value)
43530     {
43531         if(this.thousandsDelimiter) {
43532             value += "";
43533             r = new RegExp(",", "g");
43534             value = value.replace(r, "");
43535         }
43536         
43537         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43538         return isNaN(value) ? '' : value;
43539         
43540     },
43541     
43542     fixPrecision : function(value)
43543     {
43544         if(this.thousandsDelimiter) {
43545             value += "";
43546             r = new RegExp(",", "g");
43547             value = value.replace(r, "");
43548         }
43549         
43550         var nan = isNaN(value);
43551         
43552         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43553             return nan ? '' : value;
43554         }
43555         return parseFloat(value).toFixed(this.decimalPrecision);
43556     },
43557     
43558     decimalPrecisionFcn : function(v)
43559     {
43560         return Math.floor(v);
43561     },
43562     
43563     validateValue : function(value)
43564     {
43565         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43566             return false;
43567         }
43568         
43569         var num = this.parseValue(value);
43570         
43571         if(isNaN(num)){
43572             this.markInvalid(String.format(this.nanText, value));
43573             return false;
43574         }
43575         
43576         if(num < this.minValue){
43577             this.markInvalid(String.format(this.minText, this.minValue));
43578             return false;
43579         }
43580         
43581         if(num > this.maxValue){
43582             this.markInvalid(String.format(this.maxText, this.maxValue));
43583             return false;
43584         }
43585         
43586         return true;
43587     },
43588     
43589     validate : function()
43590     {
43591         if(this.disabled || this.allowBlank){
43592             this.markValid();
43593             return true;
43594         }
43595         
43596         var currency = this.getCurrency();
43597         
43598         if(this.validateValue(this.getRawValue()) && currency.length){
43599             this.markValid();
43600             return true;
43601         }
43602         
43603         this.markInvalid();
43604         return false;
43605     },
43606     
43607     getName: function()
43608     {
43609         return this.name;
43610     },
43611     
43612     beforeBlur : function()
43613     {
43614         if(!this.castInt){
43615             return;
43616         }
43617         
43618         var v = this.parseValue(this.getRawValue());
43619         
43620         if(v || v == 0){
43621             this.setValue(v);
43622         }
43623     },
43624     
43625     onBlur : function()
43626     {
43627         this.beforeBlur();
43628         
43629         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43630             //this.el.removeClass(this.focusClass);
43631         }
43632         
43633         this.hasFocus = false;
43634         
43635         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43636             this.validate();
43637         }
43638         
43639         var v = this.getValue();
43640         
43641         if(String(v) !== String(this.startValue)){
43642             this.fireEvent('change', this, v, this.startValue);
43643         }
43644         
43645         this.fireEvent("blur", this);
43646     },
43647     
43648     inputEl : function()
43649     {
43650         return this.el.select('.roo-money-amount-input', true).first();
43651     },
43652     
43653     currencyEl : function()
43654     {
43655         return this.el.select('.roo-money-currency-input', true).first();
43656     },
43657     
43658     hiddenEl : function()
43659     {
43660         return this.el.select('input.hidden-number-input',true).first();
43661     }
43662     
43663 });/**
43664  * @class Roo.bootstrap.BezierSignature
43665  * @extends Roo.bootstrap.Component
43666  * Bootstrap BezierSignature class
43667  * This script refer to:
43668  *    Title: Signature Pad
43669  *    Author: szimek
43670  *    Availability: https://github.com/szimek/signature_pad
43671  *
43672  * @constructor
43673  * Create a new BezierSignature
43674  * @param {Object} config The config object
43675  */
43676
43677 Roo.bootstrap.BezierSignature = function(config){
43678     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43679     this.addEvents({
43680         "resize" : true
43681     });
43682 };
43683
43684 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43685 {
43686      
43687     curve_data: [],
43688     
43689     is_empty: true,
43690     
43691     mouse_btn_down: true,
43692     
43693     /**
43694      * @cfg {int} canvas height
43695      */
43696     canvas_height: '200px',
43697     
43698     /**
43699      * @cfg {float|function} Radius of a single dot.
43700      */ 
43701     dot_size: false,
43702     
43703     /**
43704      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43705      */
43706     min_width: 0.5,
43707     
43708     /**
43709      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43710      */
43711     max_width: 2.5,
43712     
43713     /**
43714      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43715      */
43716     throttle: 16,
43717     
43718     /**
43719      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43720      */
43721     min_distance: 5,
43722     
43723     /**
43724      * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
43725      */
43726     bg_color: 'rgba(0, 0, 0, 0)',
43727     
43728     /**
43729      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43730      */
43731     dot_color: 'black',
43732     
43733     /**
43734      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43735      */ 
43736     velocity_filter_weight: 0.7,
43737     
43738     /**
43739      * @cfg {function} Callback when stroke begin. 
43740      */
43741     onBegin: false,
43742     
43743     /**
43744      * @cfg {function} Callback when stroke end.
43745      */
43746     onEnd: false,
43747     
43748     getAutoCreate : function()
43749     {
43750         var cls = 'roo-signature column';
43751         
43752         if(this.cls){
43753             cls += ' ' + this.cls;
43754         }
43755         
43756         var col_sizes = [
43757             'lg',
43758             'md',
43759             'sm',
43760             'xs'
43761         ];
43762         
43763         for(var i = 0; i < col_sizes.length; i++) {
43764             if(this[col_sizes[i]]) {
43765                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43766             }
43767         }
43768         
43769         var cfg = {
43770             tag: 'div',
43771             cls: cls,
43772             cn: [
43773                 {
43774                     tag: 'div',
43775                     cls: 'roo-signature-body',
43776                     cn: [
43777                         {
43778                             tag: 'canvas',
43779                             cls: 'roo-signature-body-canvas',
43780                             height: this.canvas_height,
43781                             width: this.canvas_width
43782                         }
43783                     ]
43784                 },
43785                 {
43786                     tag: 'input',
43787                     type: 'file',
43788                     style: 'display: none'
43789                 }
43790             ]
43791         };
43792         
43793         return cfg;
43794     },
43795     
43796     initEvents: function() 
43797     {
43798         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43799         
43800         var canvas = this.canvasEl();
43801         
43802         // mouse && touch event swapping...
43803         canvas.dom.style.touchAction = 'none';
43804         canvas.dom.style.msTouchAction = 'none';
43805         
43806         this.mouse_btn_down = false;
43807         canvas.on('mousedown', this._handleMouseDown, this);
43808         canvas.on('mousemove', this._handleMouseMove, this);
43809         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43810         
43811         if (window.PointerEvent) {
43812             canvas.on('pointerdown', this._handleMouseDown, this);
43813             canvas.on('pointermove', this._handleMouseMove, this);
43814             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43815         }
43816         
43817         if ('ontouchstart' in window) {
43818             canvas.on('touchstart', this._handleTouchStart, this);
43819             canvas.on('touchmove', this._handleTouchMove, this);
43820             canvas.on('touchend', this._handleTouchEnd, this);
43821         }
43822         
43823         Roo.EventManager.onWindowResize(this.resize, this, true);
43824         
43825         // file input event
43826         this.fileEl().on('change', this.uploadImage, this);
43827         
43828         this.clear();
43829         
43830         this.resize();
43831     },
43832     
43833     resize: function(){
43834         
43835         var canvas = this.canvasEl().dom;
43836         var ctx = this.canvasElCtx();
43837         var img_data = false;
43838         
43839         if(canvas.width > 0) {
43840             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43841         }
43842         // setting canvas width will clean img data
43843         canvas.width = 0;
43844         
43845         var style = window.getComputedStyle ? 
43846             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43847             
43848         var padding_left = parseInt(style.paddingLeft) || 0;
43849         var padding_right = parseInt(style.paddingRight) || 0;
43850         
43851         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43852         
43853         if(img_data) {
43854             ctx.putImageData(img_data, 0, 0);
43855         }
43856     },
43857     
43858     _handleMouseDown: function(e)
43859     {
43860         if (e.browserEvent.which === 1) {
43861             this.mouse_btn_down = true;
43862             this.strokeBegin(e);
43863         }
43864     },
43865     
43866     _handleMouseMove: function (e)
43867     {
43868         if (this.mouse_btn_down) {
43869             this.strokeMoveUpdate(e);
43870         }
43871     },
43872     
43873     _handleMouseUp: function (e)
43874     {
43875         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43876             this.mouse_btn_down = false;
43877             this.strokeEnd(e);
43878         }
43879     },
43880     
43881     _handleTouchStart: function (e) {
43882         
43883         e.preventDefault();
43884         if (e.browserEvent.targetTouches.length === 1) {
43885             // var touch = e.browserEvent.changedTouches[0];
43886             // this.strokeBegin(touch);
43887             
43888              this.strokeBegin(e); // assume e catching the correct xy...
43889         }
43890     },
43891     
43892     _handleTouchMove: function (e) {
43893         e.preventDefault();
43894         // var touch = event.targetTouches[0];
43895         // _this._strokeMoveUpdate(touch);
43896         this.strokeMoveUpdate(e);
43897     },
43898     
43899     _handleTouchEnd: function (e) {
43900         var wasCanvasTouched = e.target === this.canvasEl().dom;
43901         if (wasCanvasTouched) {
43902             e.preventDefault();
43903             // var touch = event.changedTouches[0];
43904             // _this._strokeEnd(touch);
43905             this.strokeEnd(e);
43906         }
43907     },
43908     
43909     reset: function () {
43910         this._lastPoints = [];
43911         this._lastVelocity = 0;
43912         this._lastWidth = (this.min_width + this.max_width) / 2;
43913         this.canvasElCtx().fillStyle = this.dot_color;
43914     },
43915     
43916     strokeMoveUpdate: function(e)
43917     {
43918         this.strokeUpdate(e);
43919         
43920         if (this.throttle) {
43921             this.throttleStroke(this.strokeUpdate, this.throttle);
43922         }
43923         else {
43924             this.strokeUpdate(e);
43925         }
43926     },
43927     
43928     strokeBegin: function(e)
43929     {
43930         var newPointGroup = {
43931             color: this.dot_color,
43932             points: []
43933         };
43934         
43935         if (typeof this.onBegin === 'function') {
43936             this.onBegin(e);
43937         }
43938         
43939         this.curve_data.push(newPointGroup);
43940         this.reset();
43941         this.strokeUpdate(e);
43942     },
43943     
43944     strokeUpdate: function(e)
43945     {
43946         var rect = this.canvasEl().dom.getBoundingClientRect();
43947         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43948         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43949         var lastPoints = lastPointGroup.points;
43950         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43951         var isLastPointTooClose = lastPoint
43952             ? point.distanceTo(lastPoint) <= this.min_distance
43953             : false;
43954         var color = lastPointGroup.color;
43955         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43956             var curve = this.addPoint(point);
43957             if (!lastPoint) {
43958                 this.drawDot({color: color, point: point});
43959             }
43960             else if (curve) {
43961                 this.drawCurve({color: color, curve: curve});
43962             }
43963             lastPoints.push({
43964                 time: point.time,
43965                 x: point.x,
43966                 y: point.y
43967             });
43968         }
43969     },
43970     
43971     strokeEnd: function(e)
43972     {
43973         this.strokeUpdate(e);
43974         if (typeof this.onEnd === 'function') {
43975             this.onEnd(e);
43976         }
43977     },
43978     
43979     addPoint:  function (point) {
43980         var _lastPoints = this._lastPoints;
43981         _lastPoints.push(point);
43982         if (_lastPoints.length > 2) {
43983             if (_lastPoints.length === 3) {
43984                 _lastPoints.unshift(_lastPoints[0]);
43985             }
43986             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43987             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43988             _lastPoints.shift();
43989             return curve;
43990         }
43991         return null;
43992     },
43993     
43994     calculateCurveWidths: function (startPoint, endPoint) {
43995         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43996             (1 - this.velocity_filter_weight) * this._lastVelocity;
43997
43998         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43999         var widths = {
44000             end: newWidth,
44001             start: this._lastWidth
44002         };
44003         
44004         this._lastVelocity = velocity;
44005         this._lastWidth = newWidth;
44006         return widths;
44007     },
44008     
44009     drawDot: function (_a) {
44010         var color = _a.color, point = _a.point;
44011         var ctx = this.canvasElCtx();
44012         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44013         ctx.beginPath();
44014         this.drawCurveSegment(point.x, point.y, width);
44015         ctx.closePath();
44016         ctx.fillStyle = color;
44017         ctx.fill();
44018     },
44019     
44020     drawCurve: function (_a) {
44021         var color = _a.color, curve = _a.curve;
44022         var ctx = this.canvasElCtx();
44023         var widthDelta = curve.endWidth - curve.startWidth;
44024         var drawSteps = Math.floor(curve.length()) * 2;
44025         ctx.beginPath();
44026         ctx.fillStyle = color;
44027         for (var i = 0; i < drawSteps; i += 1) {
44028         var t = i / drawSteps;
44029         var tt = t * t;
44030         var ttt = tt * t;
44031         var u = 1 - t;
44032         var uu = u * u;
44033         var uuu = uu * u;
44034         var x = uuu * curve.startPoint.x;
44035         x += 3 * uu * t * curve.control1.x;
44036         x += 3 * u * tt * curve.control2.x;
44037         x += ttt * curve.endPoint.x;
44038         var y = uuu * curve.startPoint.y;
44039         y += 3 * uu * t * curve.control1.y;
44040         y += 3 * u * tt * curve.control2.y;
44041         y += ttt * curve.endPoint.y;
44042         var width = curve.startWidth + ttt * widthDelta;
44043         this.drawCurveSegment(x, y, width);
44044         }
44045         ctx.closePath();
44046         ctx.fill();
44047     },
44048     
44049     drawCurveSegment: function (x, y, width) {
44050         var ctx = this.canvasElCtx();
44051         ctx.moveTo(x, y);
44052         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44053         this.is_empty = false;
44054     },
44055     
44056     clear: function()
44057     {
44058         var ctx = this.canvasElCtx();
44059         var canvas = this.canvasEl().dom;
44060         ctx.fillStyle = this.bg_color;
44061         ctx.clearRect(0, 0, canvas.width, canvas.height);
44062         ctx.fillRect(0, 0, canvas.width, canvas.height);
44063         this.curve_data = [];
44064         this.reset();
44065         this.is_empty = true;
44066     },
44067     
44068     fileEl: function()
44069     {
44070         return  this.el.select('input',true).first();
44071     },
44072     
44073     canvasEl: function()
44074     {
44075         return this.el.select('canvas',true).first();
44076     },
44077     
44078     canvasElCtx: function()
44079     {
44080         return this.el.select('canvas',true).first().dom.getContext('2d');
44081     },
44082     
44083     getImage: function(type)
44084     {
44085         if(this.is_empty) {
44086             return false;
44087         }
44088         
44089         // encryption ?
44090         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44091     },
44092     
44093     drawFromImage: function(img_src)
44094     {
44095         var img = new Image();
44096         
44097         img.onload = function(){
44098             this.canvasElCtx().drawImage(img, 0, 0);
44099         }.bind(this);
44100         
44101         img.src = img_src;
44102         
44103         this.is_empty = false;
44104     },
44105     
44106     selectImage: function()
44107     {
44108         this.fileEl().dom.click();
44109     },
44110     
44111     uploadImage: function(e)
44112     {
44113         var reader = new FileReader();
44114         
44115         reader.onload = function(e){
44116             var img = new Image();
44117             img.onload = function(){
44118                 this.reset();
44119                 this.canvasElCtx().drawImage(img, 0, 0);
44120             }.bind(this);
44121             img.src = e.target.result;
44122         }.bind(this);
44123         
44124         reader.readAsDataURL(e.target.files[0]);
44125     },
44126     
44127     // Bezier Point Constructor
44128     Point: (function () {
44129         function Point(x, y, time) {
44130             this.x = x;
44131             this.y = y;
44132             this.time = time || Date.now();
44133         }
44134         Point.prototype.distanceTo = function (start) {
44135             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44136         };
44137         Point.prototype.equals = function (other) {
44138             return this.x === other.x && this.y === other.y && this.time === other.time;
44139         };
44140         Point.prototype.velocityFrom = function (start) {
44141             return this.time !== start.time
44142             ? this.distanceTo(start) / (this.time - start.time)
44143             : 0;
44144         };
44145         return Point;
44146     }()),
44147     
44148     
44149     // Bezier Constructor
44150     Bezier: (function () {
44151         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44152             this.startPoint = startPoint;
44153             this.control2 = control2;
44154             this.control1 = control1;
44155             this.endPoint = endPoint;
44156             this.startWidth = startWidth;
44157             this.endWidth = endWidth;
44158         }
44159         Bezier.fromPoints = function (points, widths, scope) {
44160             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44161             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44162             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44163         };
44164         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44165             var dx1 = s1.x - s2.x;
44166             var dy1 = s1.y - s2.y;
44167             var dx2 = s2.x - s3.x;
44168             var dy2 = s2.y - s3.y;
44169             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44170             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44171             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44172             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44173             var dxm = m1.x - m2.x;
44174             var dym = m1.y - m2.y;
44175             var k = l2 / (l1 + l2);
44176             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44177             var tx = s2.x - cm.x;
44178             var ty = s2.y - cm.y;
44179             return {
44180                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44181                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44182             };
44183         };
44184         Bezier.prototype.length = function () {
44185             var steps = 10;
44186             var length = 0;
44187             var px;
44188             var py;
44189             for (var i = 0; i <= steps; i += 1) {
44190                 var t = i / steps;
44191                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44192                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44193                 if (i > 0) {
44194                     var xdiff = cx - px;
44195                     var ydiff = cy - py;
44196                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44197                 }
44198                 px = cx;
44199                 py = cy;
44200             }
44201             return length;
44202         };
44203         Bezier.prototype.point = function (t, start, c1, c2, end) {
44204             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44205             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44206             + (3.0 * c2 * (1.0 - t) * t * t)
44207             + (end * t * t * t);
44208         };
44209         return Bezier;
44210     }()),
44211     
44212     throttleStroke: function(fn, wait) {
44213       if (wait === void 0) { wait = 250; }
44214       var previous = 0;
44215       var timeout = null;
44216       var result;
44217       var storedContext;
44218       var storedArgs;
44219       var later = function () {
44220           previous = Date.now();
44221           timeout = null;
44222           result = fn.apply(storedContext, storedArgs);
44223           if (!timeout) {
44224               storedContext = null;
44225               storedArgs = [];
44226           }
44227       };
44228       return function wrapper() {
44229           var args = [];
44230           for (var _i = 0; _i < arguments.length; _i++) {
44231               args[_i] = arguments[_i];
44232           }
44233           var now = Date.now();
44234           var remaining = wait - (now - previous);
44235           storedContext = this;
44236           storedArgs = args;
44237           if (remaining <= 0 || remaining > wait) {
44238               if (timeout) {
44239                   clearTimeout(timeout);
44240                   timeout = null;
44241               }
44242               previous = now;
44243               result = fn.apply(storedContext, storedArgs);
44244               if (!timeout) {
44245                   storedContext = null;
44246                   storedArgs = [];
44247               }
44248           }
44249           else if (!timeout) {
44250               timeout = window.setTimeout(later, remaining);
44251           }
44252           return result;
44253       };
44254   }
44255   
44256 });
44257
44258  
44259
44260