Roo/bootstrap/Popover.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 {Boolean} bodyOverflow should the body element have overflow auto added default false
3951  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953  * @cfg {String} size (sm|lg|xl) default empty
3954  * @cfg {Number} max_width set the max width of modal
3955  * @cfg {Boolean} editableTitle can the title be edited
3956
3957  *
3958  *
3959  * @constructor
3960  * Create a new Modal Dialog
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.Modal = function(config){
3965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3966     this.addEvents({
3967         // raw events
3968         /**
3969          * @event btnclick
3970          * The raw btnclick event for the button
3971          * @param {Roo.EventObject} e
3972          */
3973         "btnclick" : true,
3974         /**
3975          * @event resize
3976          * Fire when dialog resize
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} e
3979          */
3980         "resize" : true,
3981         /**
3982          * @event titlechanged
3983          * Fire when the editable title has been changed
3984          * @param {Roo.bootstrap.Modal} this
3985          * @param {Roo.EventObject} value
3986          */
3987         "titlechanged" : true 
3988         
3989     });
3990     this.buttons = this.buttons || [];
3991
3992     if (this.tmpl) {
3993         this.tmpl = Roo.factory(this.tmpl);
3994     }
3995
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3999
4000     title : 'test dialog',
4001
4002     buttons : false,
4003
4004     // set on load...
4005
4006     html: false,
4007
4008     tmp: false,
4009
4010     specificTitle: false,
4011
4012     buttonPosition: 'right',
4013
4014     allow_close : true,
4015
4016     animate : true,
4017
4018     fitwindow: false,
4019     
4020      // private
4021     dialogEl: false,
4022     bodyEl:  false,
4023     footerEl:  false,
4024     titleEl:  false,
4025     closeEl:  false,
4026
4027     size: '',
4028     
4029     max_width: 0,
4030     
4031     max_height: 0,
4032     
4033     fit_content: false,
4034     editableTitle  : false,
4035
4036     onRender : function(ct, position)
4037     {
4038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4039
4040         if(!this.el){
4041             var cfg = Roo.apply({},  this.getAutoCreate());
4042             cfg.id = Roo.id();
4043             //if(!cfg.name){
4044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045             //}
4046             //if (!cfg.name.length) {
4047             //    delete cfg.name;
4048            // }
4049             if (this.cls) {
4050                 cfg.cls += ' ' + this.cls;
4051             }
4052             if (this.style) {
4053                 cfg.style = this.style;
4054             }
4055             this.el = Roo.get(document.body).createChild(cfg, position);
4056         }
4057         //var type = this.el.dom.type;
4058
4059
4060         if(this.tabIndex !== undefined){
4061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4062         }
4063
4064         this.dialogEl = this.el.select('.modal-dialog',true).first();
4065         this.bodyEl = this.el.select('.modal-body',true).first();
4066         this.closeEl = this.el.select('.modal-header .close', true).first();
4067         this.headerEl = this.el.select('.modal-header',true).first();
4068         this.titleEl = this.el.select('.modal-title',true).first();
4069         this.footerEl = this.el.select('.modal-footer',true).first();
4070
4071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072         
4073         //this.el.addClass("x-dlg-modal");
4074
4075         if (this.buttons.length) {
4076             Roo.each(this.buttons, function(bb) {
4077                 var b = Roo.apply({}, bb);
4078                 b.xns = b.xns || Roo.bootstrap;
4079                 b.xtype = b.xtype || 'Button';
4080                 if (typeof(b.listeners) == 'undefined') {
4081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4082                 }
4083
4084                 var btn = Roo.factory(b);
4085
4086                 btn.render(this.getButtonContainer());
4087
4088             },this);
4089         }
4090         // render the children.
4091         var nitems = [];
4092
4093         if(typeof(this.items) != 'undefined'){
4094             var items = this.items;
4095             delete this.items;
4096
4097             for(var i =0;i < items.length;i++) {
4098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4099             }
4100         }
4101
4102         this.items = nitems;
4103
4104         // where are these used - they used to be body/close/footer
4105
4106
4107         this.initEvents();
4108         //this.el.addClass([this.fieldClass, this.cls]);
4109
4110     },
4111
4112     getAutoCreate : function()
4113     {
4114         // we will default to modal-body-overflow - might need to remove or make optional later.
4115         var bdy = {
4116                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4117                 html : this.html || ''
4118         };
4119
4120         var title = {
4121             tag: 'h5',
4122             cls : 'modal-title',
4123             html : this.title
4124         };
4125
4126         if(this.specificTitle){ // WTF is this?
4127             title = this.title;
4128         }
4129
4130         var header = [];
4131         if (this.allow_close && Roo.bootstrap.version == 3) {
4132             header.push({
4133                 tag: 'button',
4134                 cls : 'close',
4135                 html : '&times'
4136             });
4137         }
4138
4139         header.push(title);
4140
4141         if (this.editableTitle) {
4142             header.push({
4143                 cls: 'form-control roo-editable-title d-none',
4144                 tag: 'input',
4145                 type: 'text'
4146             });
4147         }
4148         
4149         if (this.allow_close && Roo.bootstrap.version == 4) {
4150             header.push({
4151                 tag: 'button',
4152                 cls : 'close',
4153                 html : '&times'
4154             });
4155         }
4156         
4157         var size = '';
4158
4159         if(this.size.length){
4160             size = 'modal-' + this.size;
4161         }
4162         
4163         var footer = Roo.bootstrap.version == 3 ?
4164             {
4165                 cls : 'modal-footer',
4166                 cn : [
4167                     {
4168                         tag: 'div',
4169                         cls: 'btn-' + this.buttonPosition
4170                     }
4171                 ]
4172
4173             } :
4174             {  // BS4 uses mr-auto on left buttons....
4175                 cls : 'modal-footer'
4176             };
4177
4178             
4179
4180         
4181         
4182         var modal = {
4183             cls: "modal",
4184              cn : [
4185                 {
4186                     cls: "modal-dialog " + size,
4187                     cn : [
4188                         {
4189                             cls : "modal-content",
4190                             cn : [
4191                                 {
4192                                     cls : 'modal-header',
4193                                     cn : header
4194                                 },
4195                                 bdy,
4196                                 footer
4197                             ]
4198
4199                         }
4200                     ]
4201
4202                 }
4203             ]
4204         };
4205
4206         if(this.animate){
4207             modal.cls += ' fade';
4208         }
4209
4210         return modal;
4211
4212     },
4213     getChildContainer : function() {
4214
4215          return this.bodyEl;
4216
4217     },
4218     getButtonContainer : function() {
4219         
4220          return Roo.bootstrap.version == 4 ?
4221             this.el.select('.modal-footer',true).first()
4222             : this.el.select('.modal-footer div',true).first();
4223
4224     },
4225     initEvents : function()
4226     {
4227         if (this.allow_close) {
4228             this.closeEl.on('click', this.hide, this);
4229         }
4230         Roo.EventManager.onWindowResize(this.resize, this, true);
4231         if (this.editableTitle) {
4232             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4233             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234             this.headerEditEl.on('keyup', function(e) {
4235                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236                         this.toggleHeaderInput(false)
4237                     }
4238                 }, this);
4239             this.headerEditEl.on('blur', function(e) {
4240                 this.toggleHeaderInput(false)
4241             },this);
4242         }
4243
4244     },
4245   
4246
4247     resize : function()
4248     {
4249         this.maskEl.setSize(
4250             Roo.lib.Dom.getViewWidth(true),
4251             Roo.lib.Dom.getViewHeight(true)
4252         );
4253         
4254         if (this.fitwindow) {
4255             
4256            this.dialogEl.setStyle( { 'max-width' : '100%' });
4257             this.setSize(
4258                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4260             );
4261             return;
4262         }
4263         
4264         if(this.max_width !== 0) {
4265             
4266             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4267             
4268             if(this.height) {
4269                 this.setSize(w, this.height);
4270                 return;
4271             }
4272             
4273             if(this.max_height) {
4274                 this.setSize(w,Math.min(
4275                     this.max_height,
4276                     Roo.lib.Dom.getViewportHeight(true) - 60
4277                 ));
4278                 
4279                 return;
4280             }
4281             
4282             if(!this.fit_content) {
4283                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4284                 return;
4285             }
4286             
4287             this.setSize(w, Math.min(
4288                 60 +
4289                 this.headerEl.getHeight() + 
4290                 this.footerEl.getHeight() + 
4291                 this.getChildHeight(this.bodyEl.dom.childNodes),
4292                 Roo.lib.Dom.getViewportHeight(true) - 60)
4293             );
4294         }
4295         
4296     },
4297
4298     setSize : function(w,h)
4299     {
4300         if (!w && !h) {
4301             return;
4302         }
4303         
4304         this.resizeTo(w,h);
4305     },
4306
4307     show : function() {
4308
4309         if (!this.rendered) {
4310             this.render();
4311         }
4312         this.toggleHeaderInput(false);
4313         //this.el.setStyle('display', 'block');
4314         this.el.removeClass('hideing');
4315         this.el.dom.style.display='block';
4316         
4317         Roo.get(document.body).addClass('modal-open');
4318  
4319         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4320             
4321             (function(){
4322                 this.el.addClass('show');
4323                 this.el.addClass('in');
4324             }).defer(50, this);
4325         }else{
4326             this.el.addClass('show');
4327             this.el.addClass('in');
4328         }
4329
4330         // not sure how we can show data in here..
4331         //if (this.tmpl) {
4332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4333         //}
4334
4335         Roo.get(document.body).addClass("x-body-masked");
4336         
4337         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4338         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339         this.maskEl.dom.style.display = 'block';
4340         this.maskEl.addClass('show');
4341         
4342         
4343         this.resize();
4344         
4345         this.fireEvent('show', this);
4346
4347         // set zindex here - otherwise it appears to be ignored...
4348         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4349
4350         (function () {
4351             this.items.forEach( function(e) {
4352                 e.layout ? e.layout() : false;
4353
4354             });
4355         }).defer(100,this);
4356
4357     },
4358     hide : function()
4359     {
4360         if(this.fireEvent("beforehide", this) !== false){
4361             
4362             this.maskEl.removeClass('show');
4363             
4364             this.maskEl.dom.style.display = '';
4365             Roo.get(document.body).removeClass("x-body-masked");
4366             this.el.removeClass('in');
4367             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368
4369             if(this.animate){ // why
4370                 this.el.addClass('hideing');
4371                 this.el.removeClass('show');
4372                 (function(){
4373                     if (!this.el.hasClass('hideing')) {
4374                         return; // it's been shown again...
4375                     }
4376                     
4377                     this.el.dom.style.display='';
4378
4379                     Roo.get(document.body).removeClass('modal-open');
4380                     this.el.removeClass('hideing');
4381                 }).defer(150,this);
4382                 
4383             }else{
4384                 this.el.removeClass('show');
4385                 this.el.dom.style.display='';
4386                 Roo.get(document.body).removeClass('modal-open');
4387
4388             }
4389             this.fireEvent('hide', this);
4390         }
4391     },
4392     isVisible : function()
4393     {
4394         
4395         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4396         
4397     },
4398
4399     addButton : function(str, cb)
4400     {
4401
4402
4403         var b = Roo.apply({}, { html : str } );
4404         b.xns = b.xns || Roo.bootstrap;
4405         b.xtype = b.xtype || 'Button';
4406         if (typeof(b.listeners) == 'undefined') {
4407             b.listeners = { click : cb.createDelegate(this)  };
4408         }
4409
4410         var btn = Roo.factory(b);
4411
4412         btn.render(this.getButtonContainer());
4413
4414         return btn;
4415
4416     },
4417
4418     setDefaultButton : function(btn)
4419     {
4420         //this.el.select('.modal-footer').()
4421     },
4422
4423     resizeTo: function(w,h)
4424     {
4425         this.dialogEl.setWidth(w);
4426         
4427         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4428
4429         this.bodyEl.setHeight(h - diff);
4430         
4431         this.fireEvent('resize', this);
4432     },
4433     
4434     setContentSize  : function(w, h)
4435     {
4436
4437     },
4438     onButtonClick: function(btn,e)
4439     {
4440         //Roo.log([a,b,c]);
4441         this.fireEvent('btnclick', btn.name, e);
4442     },
4443      /**
4444      * Set the title of the Dialog
4445      * @param {String} str new Title
4446      */
4447     setTitle: function(str) {
4448         this.titleEl.dom.innerHTML = str;
4449         this.title = str;
4450     },
4451     /**
4452      * Set the body of the Dialog
4453      * @param {String} str new Title
4454      */
4455     setBody: function(str) {
4456         this.bodyEl.dom.innerHTML = str;
4457     },
4458     /**
4459      * Set the body of the Dialog using the template
4460      * @param {Obj} data - apply this data to the template and replace the body contents.
4461      */
4462     applyBody: function(obj)
4463     {
4464         if (!this.tmpl) {
4465             Roo.log("Error - using apply Body without a template");
4466             //code
4467         }
4468         this.tmpl.overwrite(this.bodyEl, obj);
4469     },
4470     
4471     getChildHeight : function(child_nodes)
4472     {
4473         if(
4474             !child_nodes ||
4475             child_nodes.length == 0
4476         ) {
4477             return 0;
4478         }
4479         
4480         var child_height = 0;
4481         
4482         for(var i = 0; i < child_nodes.length; i++) {
4483             
4484             /*
4485             * for modal with tabs...
4486             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487                 
4488                 var layout_childs = child_nodes[i].childNodes;
4489                 
4490                 for(var j = 0; j < layout_childs.length; j++) {
4491                     
4492                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493                         
4494                         var layout_body_childs = layout_childs[j].childNodes;
4495                         
4496                         for(var k = 0; k < layout_body_childs.length; k++) {
4497                             
4498                             if(layout_body_childs[k].classList.contains('navbar')) {
4499                                 child_height += layout_body_childs[k].offsetHeight;
4500                                 continue;
4501                             }
4502                             
4503                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504                                 
4505                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506                                 
4507                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508                                     
4509                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4511                                         continue;
4512                                     }
4513                                     
4514                                 }
4515                                 
4516                             }
4517                             
4518                         }
4519                     }
4520                 }
4521                 continue;
4522             }
4523             */
4524             
4525             child_height += child_nodes[i].offsetHeight;
4526             // Roo.log(child_nodes[i].offsetHeight);
4527         }
4528         
4529         return child_height;
4530     },
4531     toggleHeaderInput : function(is_edit)
4532     {
4533         if (!this.editableTitle) {
4534             return; // not editable.
4535         }
4536         if (is_edit && this.is_header_editing) {
4537             return; // already editing..
4538         }
4539         if (is_edit) {
4540     
4541             this.headerEditEl.dom.value = this.title;
4542             this.headerEditEl.removeClass('d-none');
4543             this.headerEditEl.dom.focus();
4544             this.titleEl.addClass('d-none');
4545             
4546             this.is_header_editing = true;
4547             return
4548         }
4549         // flip back to not editing.
4550         this.title = this.headerEditEl.dom.value;
4551         this.headerEditEl.addClass('d-none');
4552         this.titleEl.removeClass('d-none');
4553         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554         this.is_header_editing = false;
4555         this.fireEvent('titlechanged', this, this.title);
4556     
4557             
4558         
4559     }
4560
4561 });
4562
4563
4564 Roo.apply(Roo.bootstrap.Modal,  {
4565     /**
4566          * Button config that displays a single OK button
4567          * @type Object
4568          */
4569         OK :  [{
4570             name : 'ok',
4571             weight : 'primary',
4572             html : 'OK'
4573         }],
4574         /**
4575          * Button config that displays Yes and No buttons
4576          * @type Object
4577          */
4578         YESNO : [
4579             {
4580                 name  : 'no',
4581                 html : 'No'
4582             },
4583             {
4584                 name  :'yes',
4585                 weight : 'primary',
4586                 html : 'Yes'
4587             }
4588         ],
4589
4590         /**
4591          * Button config that displays OK and Cancel buttons
4592          * @type Object
4593          */
4594         OKCANCEL : [
4595             {
4596                name : 'cancel',
4597                 html : 'Cancel'
4598             },
4599             {
4600                 name : 'ok',
4601                 weight : 'primary',
4602                 html : 'OK'
4603             }
4604         ],
4605         /**
4606          * Button config that displays Yes, No and Cancel buttons
4607          * @type Object
4608          */
4609         YESNOCANCEL : [
4610             {
4611                 name : 'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             },
4615             {
4616                 name : 'no',
4617                 html : 'No'
4618             },
4619             {
4620                 name : 'cancel',
4621                 html : 'Cancel'
4622             }
4623         ],
4624         
4625         zIndex : 10001
4626 });
4627
4628 /*
4629  * - LGPL
4630  *
4631  * messagebox - can be used as a replace
4632  * 
4633  */
4634 /**
4635  * @class Roo.MessageBox
4636  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4637  * Example usage:
4638  *<pre><code>
4639 // Basic alert:
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644     if (btn == 'ok'){
4645         // process text value...
4646     }
4647 });
4648
4649 // Show a dialog using config options:
4650 Roo.Msg.show({
4651    title:'Save Changes?',
4652    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653    buttons: Roo.Msg.YESNOCANCEL,
4654    fn: processResult,
4655    animEl: 'elId'
4656 });
4657 </code></pre>
4658  * @singleton
4659  */
4660 Roo.bootstrap.MessageBox = function(){
4661     var dlg, opt, mask, waitTimer;
4662     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663     var buttons, activeTextEl, bwidth;
4664
4665     
4666     // private
4667     var handleButton = function(button){
4668         dlg.hide();
4669         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4670     };
4671
4672     // private
4673     var handleHide = function(){
4674         if(opt && opt.cls){
4675             dlg.el.removeClass(opt.cls);
4676         }
4677         //if(waitTimer){
4678         //    Roo.TaskMgr.stop(waitTimer);
4679         //    waitTimer = null;
4680         //}
4681     };
4682
4683     // private
4684     var updateButtons = function(b){
4685         var width = 0;
4686         if(!b){
4687             buttons["ok"].hide();
4688             buttons["cancel"].hide();
4689             buttons["yes"].hide();
4690             buttons["no"].hide();
4691             dlg.footerEl.hide();
4692             
4693             return width;
4694         }
4695         dlg.footerEl.show();
4696         for(var k in buttons){
4697             if(typeof buttons[k] != "function"){
4698                 if(b[k]){
4699                     buttons[k].show();
4700                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701                     width += buttons[k].el.getWidth()+15;
4702                 }else{
4703                     buttons[k].hide();
4704                 }
4705             }
4706         }
4707         return width;
4708     };
4709
4710     // private
4711     var handleEsc = function(d, k, e){
4712         if(opt && opt.closable !== false){
4713             dlg.hide();
4714         }
4715         if(e){
4716             e.stopEvent();
4717         }
4718     };
4719
4720     return {
4721         /**
4722          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723          * @return {Roo.BasicDialog} The BasicDialog element
4724          */
4725         getDialog : function(){
4726            if(!dlg){
4727                 dlg = new Roo.bootstrap.Modal( {
4728                     //draggable: true,
4729                     //resizable:false,
4730                     //constraintoviewport:false,
4731                     //fixedcenter:true,
4732                     //collapsible : false,
4733                     //shim:true,
4734                     //modal: true,
4735                 //    width: 'auto',
4736                   //  height:100,
4737                     //buttonAlign:"center",
4738                     closeClick : function(){
4739                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4740                             handleButton("no");
4741                         }else{
4742                             handleButton("cancel");
4743                         }
4744                     }
4745                 });
4746                 dlg.render();
4747                 dlg.on("hide", handleHide);
4748                 mask = dlg.mask;
4749                 //dlg.addKeyListener(27, handleEsc);
4750                 buttons = {};
4751                 this.buttons = buttons;
4752                 var bt = this.buttonText;
4753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757                 //Roo.log(buttons);
4758                 bodyEl = dlg.bodyEl.createChild({
4759
4760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761                         '<textarea class="roo-mb-textarea"></textarea>' +
4762                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4763                 });
4764                 msgEl = bodyEl.dom.firstChild;
4765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766                 textboxEl.enableDisplayMode();
4767                 textboxEl.addKeyListener([10,13], function(){
4768                     if(dlg.isVisible() && opt && opt.buttons){
4769                         if(opt.buttons.ok){
4770                             handleButton("ok");
4771                         }else if(opt.buttons.yes){
4772                             handleButton("yes");
4773                         }
4774                     }
4775                 });
4776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777                 textareaEl.enableDisplayMode();
4778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779                 progressEl.enableDisplayMode();
4780                 
4781                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782                 var pf = progressEl.dom.firstChild;
4783                 if (pf) {
4784                     pp = Roo.get(pf.firstChild);
4785                     pp.setHeight(pf.offsetHeight);
4786                 }
4787                 
4788             }
4789             return dlg;
4790         },
4791
4792         /**
4793          * Updates the message box body text
4794          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795          * the XHTML-compliant non-breaking space character '&amp;#160;')
4796          * @return {Roo.MessageBox} This message box
4797          */
4798         updateText : function(text)
4799         {
4800             if(!dlg.isVisible() && !opt.width){
4801                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803             }
4804             msgEl.innerHTML = text || '&#160;';
4805       
4806             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808             var w = Math.max(
4809                     Math.min(opt.width || cw , this.maxWidth), 
4810                     Math.max(opt.minWidth || this.minWidth, bwidth)
4811             );
4812             if(opt.prompt){
4813                 activeTextEl.setWidth(w);
4814             }
4815             if(dlg.isVisible()){
4816                 dlg.fixedcenter = false;
4817             }
4818             // to big, make it scroll. = But as usual stupid IE does not support
4819             // !important..
4820             
4821             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.height = '';
4826                 bodyEl.dom.style.overflowY = '';
4827             }
4828             if (cw > w) {
4829                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830             } else {
4831                 bodyEl.dom.style.overflowX = '';
4832             }
4833             
4834             dlg.setContentSize(w, bodyEl.getHeight());
4835             if(dlg.isVisible()){
4836                 dlg.fixedcenter = true;
4837             }
4838             return this;
4839         },
4840
4841         /**
4842          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4843          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846          * @return {Roo.MessageBox} This message box
4847          */
4848         updateProgress : function(value, text){
4849             if(text){
4850                 this.updateText(text);
4851             }
4852             
4853             if (pp) { // weird bug on my firefox - for some reason this is not defined
4854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4856             }
4857             return this;
4858         },        
4859
4860         /**
4861          * Returns true if the message box is currently displayed
4862          * @return {Boolean} True if the message box is visible, else false
4863          */
4864         isVisible : function(){
4865             return dlg && dlg.isVisible();  
4866         },
4867
4868         /**
4869          * Hides the message box if it is displayed
4870          */
4871         hide : function(){
4872             if(this.isVisible()){
4873                 dlg.hide();
4874             }  
4875         },
4876
4877         /**
4878          * Displays a new message box, or reinitializes an existing message box, based on the config options
4879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880          * The following config object properties are supported:
4881          * <pre>
4882 Property    Type             Description
4883 ----------  ---------------  ------------------------------------------------------------------------------------
4884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4885                                    closes (defaults to undefined)
4886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4889                                    progress and wait dialogs will ignore this property and always hide the
4890                                    close button as they can only be closed programmatically.
4891 cls               String           A custom CSS class to apply to the message box element
4892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4893                                    displayed (defaults to 75)
4894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4895                                    function will be btn (the name of the button that was clicked, if applicable,
4896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4897                                    Progress and wait dialogs will ignore this option since they do not respond to
4898                                    user actions and can only be closed programmatically, so any required function
4899                                    should be called by the same code after it closes the dialog.
4900 icon              String           A CSS class that provides a background image to be used as an icon for
4901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4904 modal             Boolean          False to allow user interaction with the page while the message box is
4905                                    displayed (defaults to true)
4906 msg               String           A string that will replace the existing message box body text (defaults
4907                                    to the XHTML-compliant non-breaking space character '&#160;')
4908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4909 progress          Boolean          True to display a progress bar (defaults to false)
4910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4913 title             String           The title text
4914 value             String           The string value to set into the active textbox element if displayed
4915 wait              Boolean          True to display a progress bar (defaults to false)
4916 width             Number           The width of the dialog in pixels
4917 </pre>
4918          *
4919          * Example usage:
4920          * <pre><code>
4921 Roo.Msg.show({
4922    title: 'Address',
4923    msg: 'Please enter your address:',
4924    width: 300,
4925    buttons: Roo.MessageBox.OKCANCEL,
4926    multiline: true,
4927    fn: saveAddress,
4928    animEl: 'addAddressBtn'
4929 });
4930 </code></pre>
4931          * @param {Object} config Configuration options
4932          * @return {Roo.MessageBox} This message box
4933          */
4934         show : function(options)
4935         {
4936             
4937             // this causes nightmares if you show one dialog after another
4938             // especially on callbacks..
4939              
4940             if(this.isVisible()){
4941                 
4942                 this.hide();
4943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4945                 Roo.log("New Dialog Message:" +  options.msg )
4946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4948                 
4949             }
4950             var d = this.getDialog();
4951             opt = options;
4952             d.setTitle(opt.title || "&#160;");
4953             d.closeEl.setDisplayed(opt.closable !== false);
4954             activeTextEl = textboxEl;
4955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4956             if(opt.prompt){
4957                 if(opt.multiline){
4958                     textboxEl.hide();
4959                     textareaEl.show();
4960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4961                         opt.multiline : this.defaultTextHeight);
4962                     activeTextEl = textareaEl;
4963                 }else{
4964                     textboxEl.show();
4965                     textareaEl.hide();
4966                 }
4967             }else{
4968                 textboxEl.hide();
4969                 textareaEl.hide();
4970             }
4971             progressEl.setDisplayed(opt.progress === true);
4972             if (opt.progress) {
4973                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974             }
4975             this.updateProgress(0);
4976             activeTextEl.dom.value = opt.value || "";
4977             if(opt.prompt){
4978                 dlg.setDefaultButton(activeTextEl);
4979             }else{
4980                 var bs = opt.buttons;
4981                 var db = null;
4982                 if(bs && bs.ok){
4983                     db = buttons["ok"];
4984                 }else if(bs && bs.yes){
4985                     db = buttons["yes"];
4986                 }
4987                 dlg.setDefaultButton(db);
4988             }
4989             bwidth = updateButtons(opt.buttons);
4990             this.updateText(opt.msg);
4991             if(opt.cls){
4992                 d.el.addClass(opt.cls);
4993             }
4994             d.proxyDrag = opt.proxyDrag === true;
4995             d.modal = opt.modal !== false;
4996             d.mask = opt.modal !== false ? mask : false;
4997             if(!d.isVisible()){
4998                 // force it to the end of the z-index stack so it gets a cursor in FF
4999                 document.body.appendChild(dlg.el.dom);
5000                 d.animateTarget = null;
5001                 d.show(options.animEl);
5002             }
5003             return this;
5004         },
5005
5006         /**
5007          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5008          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009          * and closing the message box when the process is complete.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         progress : function(title, msg){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: false,
5019                 progress:true,
5020                 closable:false,
5021                 minWidth: this.minProgressWidth,
5022                 modal : true
5023             });
5024             return this;
5025         },
5026
5027         /**
5028          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029          * If a callback function is passed it will be called after the user clicks the button, and the
5030          * id of the button that was clicked will be passed as the only parameter to the callback
5031          * (could also be the top-right close button).
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035          * @param {Object} scope (optional) The scope of the callback function
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         alert : function(title, msg, fn, scope)
5039         {
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: this.OK,
5044                 fn: fn,
5045                 closable : false,
5046                 scope : scope,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055          * You are responsible for closing the message box when the process is complete.
5056          * @param {String} msg The message box body text
5057          * @param {String} title (optional) The title bar text
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         wait : function(msg, title){
5061             this.show({
5062                 title : title,
5063                 msg : msg,
5064                 buttons: false,
5065                 closable:false,
5066                 progress:true,
5067                 modal:true,
5068                 width:300,
5069                 wait:true
5070             });
5071             waitTimer = Roo.TaskMgr.start({
5072                 run: function(i){
5073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074                 },
5075                 interval: 1000
5076             });
5077             return this;
5078         },
5079
5080         /**
5081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084          * @param {String} title The title bar text
5085          * @param {String} msg The message box body text
5086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087          * @param {Object} scope (optional) The scope of the callback function
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         confirm : function(title, msg, fn, scope){
5091             this.show({
5092                 title : title,
5093                 msg : msg,
5094                 buttons: this.YESNO,
5095                 fn: fn,
5096                 scope : scope,
5097                 modal : true
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106          * (could also be the top-right close button) and the text that was entered will be passed as the two
5107          * parameters to the callback.
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         prompt : function(title, msg, fn, scope, multiline){
5117             this.show({
5118                 title : title,
5119                 msg : msg,
5120                 buttons: this.OKCANCEL,
5121                 fn: fn,
5122                 minWidth:250,
5123                 scope : scope,
5124                 prompt:true,
5125                 multiline: multiline,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Button config that displays a single OK button
5133          * @type Object
5134          */
5135         OK : {ok:true},
5136         /**
5137          * Button config that displays Yes and No buttons
5138          * @type Object
5139          */
5140         YESNO : {yes:true, no:true},
5141         /**
5142          * Button config that displays OK and Cancel buttons
5143          * @type Object
5144          */
5145         OKCANCEL : {ok:true, cancel:true},
5146         /**
5147          * Button config that displays Yes, No and Cancel buttons
5148          * @type Object
5149          */
5150         YESNOCANCEL : {yes:true, no:true, cancel:true},
5151
5152         /**
5153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5154          * @type Number
5155          */
5156         defaultTextHeight : 75,
5157         /**
5158          * The maximum width in pixels of the message box (defaults to 600)
5159          * @type Number
5160          */
5161         maxWidth : 600,
5162         /**
5163          * The minimum width in pixels of the message box (defaults to 100)
5164          * @type Number
5165          */
5166         minWidth : 100,
5167         /**
5168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5170          * @type Number
5171          */
5172         minProgressWidth : 250,
5173         /**
5174          * An object containing the default button text strings that can be overriden for localized language support.
5175          * Supported properties are: ok, cancel, yes and no.
5176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5177          * @type Object
5178          */
5179         buttonText : {
5180             ok : "OK",
5181             cancel : "Cancel",
5182             yes : "Yes",
5183             no : "No"
5184         }
5185     };
5186 }();
5187
5188 /**
5189  * Shorthand for {@link Roo.MessageBox}
5190  */
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5193 /*
5194  * - LGPL
5195  *
5196  * navbar
5197  * 
5198  */
5199
5200 /**
5201  * @class Roo.bootstrap.Navbar
5202  * @extends Roo.bootstrap.Component
5203  * Bootstrap Navbar class
5204
5205  * @constructor
5206  * Create a new Navbar
5207  * @param {Object} config The config object
5208  */
5209
5210
5211 Roo.bootstrap.Navbar = function(config){
5212     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5213     this.addEvents({
5214         // raw events
5215         /**
5216          * @event beforetoggle
5217          * Fire before toggle the menu
5218          * @param {Roo.EventObject} e
5219          */
5220         "beforetoggle" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5225     
5226     
5227    
5228     // private
5229     navItems : false,
5230     loadMask : false,
5231     
5232     
5233     getAutoCreate : function(){
5234         
5235         
5236         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5237         
5238     },
5239     
5240     initEvents :function ()
5241     {
5242         //Roo.log(this.el.select('.navbar-toggle',true));
5243         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244         
5245         var mark = {
5246             tag: "div",
5247             cls:"x-dlg-mask"
5248         };
5249         
5250         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251         
5252         var size = this.el.getSize();
5253         this.maskEl.setSize(size.width, size.height);
5254         this.maskEl.enableDisplayMode("block");
5255         this.maskEl.hide();
5256         
5257         if(this.loadMask){
5258             this.maskEl.show();
5259         }
5260     },
5261     
5262     
5263     getChildContainer : function()
5264     {
5265         if (this.el && this.el.select('.collapse').getCount()) {
5266             return this.el.select('.collapse',true).first();
5267         }
5268         
5269         return this.el;
5270     },
5271     
5272     mask : function()
5273     {
5274         this.maskEl.show();
5275     },
5276     
5277     unmask : function()
5278     {
5279         this.maskEl.hide();
5280     },
5281     onToggle : function()
5282     {
5283         
5284         if(this.fireEvent('beforetoggle', this) === false){
5285             return;
5286         }
5287         var ce = this.el.select('.navbar-collapse',true).first();
5288       
5289         if (!ce.hasClass('show')) {
5290            this.expand();
5291         } else {
5292             this.collapse();
5293         }
5294         
5295         
5296     
5297     },
5298     /**
5299      * Expand the navbar pulldown 
5300      */
5301     expand : function ()
5302     {
5303        
5304         var ce = this.el.select('.navbar-collapse',true).first();
5305         if (ce.hasClass('collapsing')) {
5306             return;
5307         }
5308         ce.dom.style.height = '';
5309                // show it...
5310         ce.addClass('in'); // old...
5311         ce.removeClass('collapse');
5312         ce.addClass('show');
5313         var h = ce.getHeight();
5314         Roo.log(h);
5315         ce.removeClass('show');
5316         // at this point we should be able to see it..
5317         ce.addClass('collapsing');
5318         
5319         ce.setHeight(0); // resize it ...
5320         ce.on('transitionend', function() {
5321             //Roo.log('done transition');
5322             ce.removeClass('collapsing');
5323             ce.addClass('show');
5324             ce.removeClass('collapse');
5325
5326             ce.dom.style.height = '';
5327         }, this, { single: true} );
5328         ce.setHeight(h);
5329         ce.dom.scrollTop = 0;
5330     },
5331     /**
5332      * Collapse the navbar pulldown 
5333      */
5334     collapse : function()
5335     {
5336          var ce = this.el.select('.navbar-collapse',true).first();
5337        
5338         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339             // it's collapsed or collapsing..
5340             return;
5341         }
5342         ce.removeClass('in'); // old...
5343         ce.setHeight(ce.getHeight());
5344         ce.removeClass('show');
5345         ce.addClass('collapsing');
5346         
5347         ce.on('transitionend', function() {
5348             ce.dom.style.height = '';
5349             ce.removeClass('collapsing');
5350             ce.addClass('collapse');
5351         }, this, { single: true} );
5352         ce.setHeight(0);
5353     }
5354     
5355     
5356     
5357 });
5358
5359
5360
5361  
5362
5363  /*
5364  * - LGPL
5365  *
5366  * navbar
5367  * 
5368  */
5369
5370 /**
5371  * @class Roo.bootstrap.NavSimplebar
5372  * @extends Roo.bootstrap.Navbar
5373  * Bootstrap Sidebar class
5374  *
5375  * @cfg {Boolean} inverse is inverted color
5376  * 
5377  * @cfg {String} type (nav | pills | tabs)
5378  * @cfg {Boolean} arrangement stacked | justified
5379  * @cfg {String} align (left | right) alignment
5380  * 
5381  * @cfg {Boolean} main (true|false) main nav bar? default false
5382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383  * 
5384  * @cfg {String} tag (header|footer|nav|div) default is nav 
5385
5386  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Sidebar
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.NavSimplebar = function(config){
5396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5400     
5401     inverse: false,
5402     
5403     type: false,
5404     arrangement: '',
5405     align : false,
5406     
5407     weight : 'light',
5408     
5409     main : false,
5410     
5411     
5412     tag : false,
5413     
5414     
5415     getAutoCreate : function(){
5416         
5417         
5418         var cfg = {
5419             tag : this.tag || 'div',
5420             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421         };
5422         if (['light','white'].indexOf(this.weight) > -1) {
5423             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424         }
5425         cfg.cls += ' bg-' + this.weight;
5426         
5427         if (this.inverse) {
5428             cfg.cls += ' navbar-inverse';
5429             
5430         }
5431         
5432         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433         
5434         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5435             return cfg;
5436         }
5437         
5438         
5439     
5440         
5441         cfg.cn = [
5442             {
5443                 cls: 'nav nav-' + this.xtype,
5444                 tag : 'ul'
5445             }
5446         ];
5447         
5448          
5449         this.type = this.type || 'nav';
5450         if (['tabs','pills'].indexOf(this.type) != -1) {
5451             cfg.cn[0].cls += ' nav-' + this.type
5452         
5453         
5454         } else {
5455             if (this.type!=='nav') {
5456                 Roo.log('nav type must be nav/tabs/pills')
5457             }
5458             cfg.cn[0].cls += ' navbar-nav'
5459         }
5460         
5461         
5462         
5463         
5464         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465             cfg.cn[0].cls += ' nav-' + this.arrangement;
5466         }
5467         
5468         
5469         if (this.align === 'right') {
5470             cfg.cn[0].cls += ' navbar-right';
5471         }
5472         
5473         
5474         
5475         
5476         return cfg;
5477     
5478         
5479     }
5480     
5481     
5482     
5483 });
5484
5485
5486
5487  
5488
5489  
5490        /*
5491  * - LGPL
5492  *
5493  * navbar
5494  * navbar-fixed-top
5495  * navbar-expand-md  fixed-top 
5496  */
5497
5498 /**
5499  * @class Roo.bootstrap.NavHeaderbar
5500  * @extends Roo.bootstrap.NavSimplebar
5501  * Bootstrap Sidebar class
5502  *
5503  * @cfg {String} brand what is brand
5504  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505  * @cfg {String} brand_href href of the brand
5506  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5507  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5510  * 
5511  * @constructor
5512  * Create a new Sidebar
5513  * @param {Object} config The config object
5514  */
5515
5516
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5519       
5520 };
5521
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5523     
5524     position: '',
5525     brand: '',
5526     brand_href: false,
5527     srButton : true,
5528     autohide : false,
5529     desktopCenter : false,
5530    
5531     
5532     getAutoCreate : function(){
5533         
5534         var   cfg = {
5535             tag: this.nav || 'nav',
5536             cls: 'navbar navbar-expand-md',
5537             role: 'navigation',
5538             cn: []
5539         };
5540         
5541         var cn = cfg.cn;
5542         if (this.desktopCenter) {
5543             cn.push({cls : 'container', cn : []});
5544             cn = cn[0].cn;
5545         }
5546         
5547         if(this.srButton){
5548             var btn = {
5549                 tag: 'button',
5550                 type: 'button',
5551                 cls: 'navbar-toggle navbar-toggler',
5552                 'data-toggle': 'collapse',
5553                 cn: [
5554                     {
5555                         tag: 'span',
5556                         cls: 'sr-only',
5557                         html: 'Toggle navigation'
5558                     },
5559                     {
5560                         tag: 'span',
5561                         cls: 'icon-bar navbar-toggler-icon'
5562                     },
5563                     {
5564                         tag: 'span',
5565                         cls: 'icon-bar'
5566                     },
5567                     {
5568                         tag: 'span',
5569                         cls: 'icon-bar'
5570                     }
5571                 ]
5572             };
5573             
5574             cn.push( Roo.bootstrap.version == 4 ? btn : {
5575                 tag: 'div',
5576                 cls: 'navbar-header',
5577                 cn: [
5578                     btn
5579                 ]
5580             });
5581         }
5582         
5583         cn.push({
5584             tag: 'div',
5585             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5586             cn : []
5587         });
5588         
5589         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590         
5591         if (['light','white'].indexOf(this.weight) > -1) {
5592             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593         }
5594         cfg.cls += ' bg-' + this.weight;
5595         
5596         
5597         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599             
5600             // tag can override this..
5601             
5602             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5603         }
5604         
5605         if (this.brand !== '') {
5606             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608                 tag: 'a',
5609                 href: this.brand_href ? this.brand_href : '#',
5610                 cls: 'navbar-brand',
5611                 cn: [
5612                 this.brand
5613                 ]
5614             });
5615         }
5616         
5617         if(this.main){
5618             cfg.cls += ' main-nav';
5619         }
5620         
5621         
5622         return cfg;
5623
5624         
5625     },
5626     getHeaderChildContainer : function()
5627     {
5628         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629             return this.el.select('.navbar-header',true).first();
5630         }
5631         
5632         return this.getChildContainer();
5633     },
5634     
5635     getChildContainer : function()
5636     {
5637          
5638         return this.el.select('.roo-navbar-collapse',true).first();
5639          
5640         
5641     },
5642     
5643     initEvents : function()
5644     {
5645         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646         
5647         if (this.autohide) {
5648             
5649             var prevScroll = 0;
5650             var ft = this.el;
5651             
5652             Roo.get(document).on('scroll',function(e) {
5653                 var ns = Roo.get(document).getScroll().top;
5654                 var os = prevScroll;
5655                 prevScroll = ns;
5656                 
5657                 if(ns > os){
5658                     ft.removeClass('slideDown');
5659                     ft.addClass('slideUp');
5660                     return;
5661                 }
5662                 ft.removeClass('slideUp');
5663                 ft.addClass('slideDown');
5664                  
5665               
5666           },this);
5667         }
5668     }    
5669     
5670 });
5671
5672
5673
5674  
5675
5676  /*
5677  * - LGPL
5678  *
5679  * navbar
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.NavSidebar
5685  * @extends Roo.bootstrap.Navbar
5686  * Bootstrap Sidebar class
5687  * 
5688  * @constructor
5689  * Create a new Sidebar
5690  * @param {Object} config The config object
5691  */
5692
5693
5694 Roo.bootstrap.NavSidebar = function(config){
5695     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5696 };
5697
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5699     
5700     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701     
5702     getAutoCreate : function(){
5703         
5704         
5705         return  {
5706             tag: 'div',
5707             cls: 'sidebar sidebar-nav'
5708         };
5709     
5710         
5711     }
5712     
5713     
5714     
5715 });
5716
5717
5718
5719  
5720
5721  /*
5722  * - LGPL
5723  *
5724  * nav group
5725  * 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavGroup
5730  * @extends Roo.bootstrap.Component
5731  * Bootstrap NavGroup class
5732  * @cfg {String} align (left|right)
5733  * @cfg {Boolean} inverse
5734  * @cfg {String} type (nav|pills|tab) default nav
5735  * @cfg {String} navId - reference Id for navbar.
5736  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5737  * 
5738  * @constructor
5739  * Create a new nav group
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.NavGroup = function(config){
5744     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5745     this.navItems = [];
5746    
5747     Roo.bootstrap.NavGroup.register(this);
5748      this.addEvents({
5749         /**
5750              * @event changed
5751              * Fires when the active item changes
5752              * @param {Roo.bootstrap.NavGroup} this
5753              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5755          */
5756         'changed': true
5757      });
5758     
5759 };
5760
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5762     
5763     align: '',
5764     inverse: false,
5765     form: false,
5766     type: 'nav',
5767     navId : '',
5768     // private
5769     pilltype : true,
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032  * @cfg {Boolean} button_outline show and outlined button
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  * @cfg {String} linkcls  Link Class
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     linkcls : '',
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6111         
6112         if (this.active) {
6113             cfg.cls +=  ' active' ;
6114         }
6115         if (this.disabled) {
6116             cfg.cls += ' disabled';
6117         }
6118         
6119         // BS4 only?
6120         if (this.button_weight.length) {
6121             cfg.tag = this.href ? 'a' : 'button';
6122             cfg.html = this.html || '';
6123             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124             if (this.href) {
6125                 cfg.href = this.href;
6126             }
6127             if (this.fa) {
6128                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129             }
6130             
6131             // menu .. should add dropdown-menu class - so no need for carat..
6132             
6133             if (this.badge !== '') {
6134                  
6135                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136             }
6137             return cfg;
6138         }
6139         
6140         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141             cfg.cn = [
6142                 {
6143                     tag: this.tagtype,
6144                     href : this.href || "#",
6145                     html: this.html || ''
6146                 }
6147             ];
6148             if (this.tagtype == 'a') {
6149                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6150         
6151             }
6152             if (this.icon) {
6153                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if (this.fa) {
6156                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if(this.glyphicon) {
6159                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6160             }
6161             
6162             if (this.menu) {
6163                 
6164                 cfg.cn[0].html += " <span class='caret'></span>";
6165              
6166             }
6167             
6168             if (this.badge !== '') {
6169                  
6170                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6171             }
6172         }
6173         
6174         
6175         
6176         return cfg;
6177     },
6178     onRender : function(ct, position)
6179     {
6180        // Roo.log("Call onRender: " + this.xtype);
6181         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182             this.tag = 'div';
6183         }
6184         
6185         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186         this.navLink = this.el.select('.nav-link',true).first();
6187         return ret;
6188     },
6189       
6190     
6191     initEvents: function() 
6192     {
6193         if (typeof (this.menu) != 'undefined') {
6194             this.menu.parentType = this.xtype;
6195             this.menu.triggerEl = this.el;
6196             this.menu = this.addxtype(Roo.apply({}, this.menu));
6197         }
6198         
6199         this.el.on('click', this.onClick, this);
6200         
6201         //if(this.tagtype == 'span'){
6202         //    this.el.select('span',true).on('click', this.onClick, this);
6203         //}
6204        
6205         // at this point parent should be available..
6206         this.parent().register(this);
6207     },
6208     
6209     onClick : function(e)
6210     {
6211         if (e.getTarget('.dropdown-menu-item')) {
6212             // did you click on a menu itemm.... - then don't trigger onclick..
6213             return;
6214         }
6215         
6216         if(
6217                 this.preventDefault || 
6218                 this.href == '#' 
6219         ){
6220             Roo.log("NavItem - prevent Default?");
6221             e.preventDefault();
6222         }
6223         
6224         if (this.disabled) {
6225             return;
6226         }
6227         
6228         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229         if (tg && tg.transition) {
6230             Roo.log("waiting for the transitionend");
6231             return;
6232         }
6233         
6234         
6235         
6236         //Roo.log("fire event clicked");
6237         if(this.fireEvent('click', this, e) === false){
6238             return;
6239         };
6240         
6241         if(this.tagtype == 'span'){
6242             return;
6243         }
6244         
6245         //Roo.log(this.href);
6246         var ael = this.el.select('a',true).first();
6247         //Roo.log(ael);
6248         
6249         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252                 return; // ignore... - it's a 'hash' to another page.
6253             }
6254             Roo.log("NavItem - prevent Default?");
6255             e.preventDefault();
6256             this.scrollToElement(e);
6257         }
6258         
6259         
6260         var p =  this.parent();
6261    
6262         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263             if (typeof(p.setActiveItem) !== 'undefined') {
6264                 p.setActiveItem(this);
6265             }
6266         }
6267         
6268         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270             // remove the collapsed menu expand...
6271             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6272         }
6273     },
6274     
6275     isActive: function () {
6276         return this.active
6277     },
6278     setActive : function(state, fire, is_was_active)
6279     {
6280         if (this.active && !state && this.navId) {
6281             this.was_active = true;
6282             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283             if (nv) {
6284                 nv.clearWasActive(this);
6285             }
6286             
6287         }
6288         this.active = state;
6289         
6290         if (!state ) {
6291             this.el.removeClass('active');
6292             this.navLink ? this.navLink.removeClass('active') : false;
6293         } else if (!this.el.hasClass('active')) {
6294             
6295             this.el.addClass('active');
6296             if (Roo.bootstrap.version == 4 && this.navLink ) {
6297                 this.navLink.addClass('active');
6298             }
6299             
6300         }
6301         if (fire) {
6302             this.fireEvent('changed', this, state);
6303         }
6304         
6305         // show a panel if it's registered and related..
6306         
6307         if (!this.navId || !this.tabId || !state || is_was_active) {
6308             return;
6309         }
6310         
6311         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312         if (!tg) {
6313             return;
6314         }
6315         var pan = tg.getPanelByName(this.tabId);
6316         if (!pan) {
6317             return;
6318         }
6319         // if we can not flip to new panel - go back to old nav highlight..
6320         if (false == tg.showPanel(pan)) {
6321             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322             if (nv) {
6323                 var onav = nv.getWasActive();
6324                 if (onav) {
6325                     onav.setActive(true, false, true);
6326                 }
6327             }
6328             
6329         }
6330         
6331         
6332         
6333     },
6334      // this should not be here...
6335     setDisabled : function(state)
6336     {
6337         this.disabled = state;
6338         if (!state ) {
6339             this.el.removeClass('disabled');
6340         } else if (!this.el.hasClass('disabled')) {
6341             this.el.addClass('disabled');
6342         }
6343         
6344     },
6345     
6346     /**
6347      * Fetch the element to display the tooltip on.
6348      * @return {Roo.Element} defaults to this.el
6349      */
6350     tooltipEl : function()
6351     {
6352         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6353     },
6354     
6355     scrollToElement : function(e)
6356     {
6357         var c = document.body;
6358         
6359         /*
6360          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361          */
6362         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363             c = document.documentElement;
6364         }
6365         
6366         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367         
6368         if(!target){
6369             return;
6370         }
6371
6372         var o = target.calcOffsetsTo(c);
6373         
6374         var options = {
6375             target : target,
6376             value : o[1]
6377         };
6378         
6379         this.fireEvent('scrollto', this, options, e);
6380         
6381         Roo.get(c).scrollTo('top', options.value, true);
6382         
6383         return;
6384     }
6385 });
6386  
6387
6388  /*
6389  * - LGPL
6390  *
6391  * sidebar item
6392  *
6393  *  li
6394  *    <span> icon </span>
6395  *    <span> text </span>
6396  *    <span>badge </span>
6397  */
6398
6399 /**
6400  * @class Roo.bootstrap.NavSidebarItem
6401  * @extends Roo.bootstrap.NavItem
6402  * Bootstrap Navbar.NavSidebarItem class
6403  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404  * {Boolean} open is the menu open
6405  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407  * {String} buttonSize (sm|md|lg)the extra classes for the button
6408  * {Boolean} showArrow show arrow next to the text (default true)
6409  * @constructor
6410  * Create a new Navbar Button
6411  * @param {Object} config The config object
6412  */
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6415     this.addEvents({
6416         // raw events
6417         /**
6418          * @event click
6419          * The raw click event for the entire grid.
6420          * @param {Roo.EventObject} e
6421          */
6422         "click" : true,
6423          /**
6424             * @event changed
6425             * Fires when the active item active state changes
6426             * @param {Roo.bootstrap.NavSidebarItem} this
6427             * @param {boolean} state the new state
6428              
6429          */
6430         'changed': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6436     
6437     badgeWeight : 'default',
6438     
6439     open: false,
6440     
6441     buttonView : false,
6442     
6443     buttonWeight : 'default',
6444     
6445     buttonSize : 'md',
6446     
6447     showArrow : true,
6448     
6449     getAutoCreate : function(){
6450         
6451         
6452         var a = {
6453                 tag: 'a',
6454                 href : this.href || '#',
6455                 cls: '',
6456                 html : '',
6457                 cn : []
6458         };
6459         
6460         if(this.buttonView){
6461             a = {
6462                 tag: 'button',
6463                 href : this.href || '#',
6464                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6465                 html : this.html,
6466                 cn : []
6467             };
6468         }
6469         
6470         var cfg = {
6471             tag: 'li',
6472             cls: '',
6473             cn: [ a ]
6474         };
6475         
6476         if (this.active) {
6477             cfg.cls += ' active';
6478         }
6479         
6480         if (this.disabled) {
6481             cfg.cls += ' disabled';
6482         }
6483         if (this.open) {
6484             cfg.cls += ' open x-open';
6485         }
6486         // left icon..
6487         if (this.glyphicon || this.icon) {
6488             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6489             a.cn.push({ tag : 'i', cls : c }) ;
6490         }
6491         
6492         if(!this.buttonView){
6493             var span = {
6494                 tag: 'span',
6495                 html : this.html || ''
6496             };
6497
6498             a.cn.push(span);
6499             
6500         }
6501         
6502         if (this.badge !== '') {
6503             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6504         }
6505         
6506         if (this.menu) {
6507             
6508             if(this.showArrow){
6509                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510             }
6511             
6512             a.cls += ' dropdown-toggle treeview' ;
6513         }
6514         
6515         return cfg;
6516     },
6517     
6518     initEvents : function()
6519     { 
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         if(this.badge !== ''){
6529             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6530         }
6531         
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if(this.disabled){
6537             e.preventDefault();
6538             return;
6539         }
6540         
6541         if(this.preventDefault){
6542             e.preventDefault();
6543         }
6544         
6545         this.fireEvent('click', this, e);
6546     },
6547     
6548     disable : function()
6549     {
6550         this.setDisabled(true);
6551     },
6552     
6553     enable : function()
6554     {
6555         this.setDisabled(false);
6556     },
6557     
6558     setDisabled : function(state)
6559     {
6560         if(this.disabled == state){
6561             return;
6562         }
6563         
6564         this.disabled = state;
6565         
6566         if (state) {
6567             this.el.addClass('disabled');
6568             return;
6569         }
6570         
6571         this.el.removeClass('disabled');
6572         
6573         return;
6574     },
6575     
6576     setActive : function(state)
6577     {
6578         if(this.active == state){
6579             return;
6580         }
6581         
6582         this.active = state;
6583         
6584         if (state) {
6585             this.el.addClass('active');
6586             return;
6587         }
6588         
6589         this.el.removeClass('active');
6590         
6591         return;
6592     },
6593     
6594     isActive: function () 
6595     {
6596         return this.active;
6597     },
6598     
6599     setBadge : function(str)
6600     {
6601         if(!this.badgeEl){
6602             return;
6603         }
6604         
6605         this.badgeEl.dom.innerHTML = str;
6606     }
6607     
6608    
6609      
6610  
6611 });
6612  
6613
6614  /*
6615  * - LGPL
6616  *
6617  *  Breadcrumb Nav
6618  * 
6619  */
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6621
6622
6623 /**
6624  * @class Roo.bootstrap.breadcrumb.Nav
6625  * @extends Roo.bootstrap.Component
6626  * Bootstrap Breadcrumb Nav Class
6627  *  
6628  * @children Roo.bootstrap.breadcrumb.Item
6629  * 
6630  * @constructor
6631  * Create a new breadcrumb.Nav
6632  * @param {Object} config The config object
6633  */
6634
6635
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6638     
6639     
6640 };
6641
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6643     
6644     getAutoCreate : function()
6645     {
6646
6647         var cfg = {
6648             tag: 'nav',
6649             cn : [
6650                 {
6651                     tag : 'ol',
6652                     cls : 'breadcrumb'
6653                 }
6654             ]
6655             
6656         };
6657           
6658         return cfg;
6659     },
6660     
6661     initEvents: function()
6662     {
6663         this.olEl = this.el.select('ol',true).first();    
6664     },
6665     getChildContainer : function()
6666     {
6667         return this.olEl;  
6668     }
6669     
6670 });
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Item
6676  * 
6677  */
6678
6679
6680 /**
6681  * @class Roo.bootstrap.breadcrumb.Nav
6682  * @extends Roo.bootstrap.Component
6683  * Bootstrap Breadcrumb Nav Class
6684  *  
6685  * @children Roo.bootstrap.breadcrumb.Component
6686  * @cfg {String} html the content of the link.
6687  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688  * @cfg {Boolean} active is it active
6689
6690  * 
6691  * @constructor
6692  * Create a new breadcrumb.Nav
6693  * @param {Object} config The config object
6694  */
6695
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6698     this.addEvents({
6699         // img events
6700         /**
6701          * @event click
6702          * The img click event for the img.
6703          * @param {Roo.EventObject} e
6704          */
6705         "click" : true
6706     });
6707     
6708 };
6709
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6711     
6712     href: false,
6713     html : '',
6714     
6715     getAutoCreate : function()
6716     {
6717
6718         var cfg = {
6719             tag: 'li',
6720             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721         };
6722         if (this.href !== false) {
6723             cfg.cn = [{
6724                 tag : 'a',
6725                 href : this.href,
6726                 html : this.html
6727             }];
6728         } else {
6729             cfg.html = this.html;
6730         }
6731         
6732         return cfg;
6733     },
6734     
6735     initEvents: function()
6736     {
6737         if (this.href) {
6738             this.el.select('a', true).first().on('click',this.onClick, this)
6739         }
6740         
6741     },
6742     onClick : function(e)
6743     {
6744         e.preventDefault();
6745         this.fireEvent('click',this,  e);
6746     }
6747     
6748 });
6749
6750  /*
6751  * - LGPL
6752  *
6753  * row
6754  * 
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.Row
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Row class (contains columns...)
6761  * 
6762  * @constructor
6763  * Create a new Row
6764  * @param {Object} config The config object
6765  */
6766
6767 Roo.bootstrap.Row = function(config){
6768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 };
6770
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6772     
6773     getAutoCreate : function(){
6774        return {
6775             cls: 'row clearfix'
6776        };
6777     }
6778     
6779     
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * pagination
6788  * 
6789  */
6790
6791 /**
6792  * @class Roo.bootstrap.Pagination
6793  * @extends Roo.bootstrap.Component
6794  * Bootstrap Pagination class
6795  * @cfg {String} size xs | sm | md | lg
6796  * @cfg {Boolean} inverse false | true
6797  * 
6798  * @constructor
6799  * Create a new Pagination
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Pagination = function(config){
6804     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6808     
6809     cls: false,
6810     size: false,
6811     inverse: false,
6812     
6813     getAutoCreate : function(){
6814         var cfg = {
6815             tag: 'ul',
6816                 cls: 'pagination'
6817         };
6818         if (this.inverse) {
6819             cfg.cls += ' inverse';
6820         }
6821         if (this.html) {
6822             cfg.html=this.html;
6823         }
6824         if (this.cls) {
6825             cfg.cls += " " + this.cls;
6826         }
6827         return cfg;
6828     }
6829    
6830 });
6831
6832  
6833
6834  /*
6835  * - LGPL
6836  *
6837  * Pagination item
6838  * 
6839  */
6840
6841
6842 /**
6843  * @class Roo.bootstrap.PaginationItem
6844  * @extends Roo.bootstrap.Component
6845  * Bootstrap PaginationItem class
6846  * @cfg {String} html text
6847  * @cfg {String} href the link
6848  * @cfg {Boolean} preventDefault (true | false) default true
6849  * @cfg {Boolean} active (true | false) default false
6850  * @cfg {Boolean} disabled default false
6851  * 
6852  * 
6853  * @constructor
6854  * Create a new PaginationItem
6855  * @param {Object} config The config object
6856  */
6857
6858
6859 Roo.bootstrap.PaginationItem = function(config){
6860     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6861     this.addEvents({
6862         // raw events
6863         /**
6864          * @event click
6865          * The raw click event for the entire grid.
6866          * @param {Roo.EventObject} e
6867          */
6868         "click" : true
6869     });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6873     
6874     href : false,
6875     html : false,
6876     preventDefault: true,
6877     active : false,
6878     cls : false,
6879     disabled: false,
6880     
6881     getAutoCreate : function(){
6882         var cfg= {
6883             tag: 'li',
6884             cn: [
6885                 {
6886                     tag : 'a',
6887                     href : this.href ? this.href : '#',
6888                     html : this.html ? this.html : ''
6889                 }
6890             ]
6891         };
6892         
6893         if(this.cls){
6894             cfg.cls = this.cls;
6895         }
6896         
6897         if(this.disabled){
6898             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899         }
6900         
6901         if(this.active){
6902             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903         }
6904         
6905         return cfg;
6906     },
6907     
6908     initEvents: function() {
6909         
6910         this.el.on('click', this.onClick, this);
6911         
6912     },
6913     onClick : function(e)
6914     {
6915         Roo.log('PaginationItem on click ');
6916         if(this.preventDefault){
6917             e.preventDefault();
6918         }
6919         
6920         if(this.disabled){
6921             return;
6922         }
6923         
6924         this.fireEvent('click', this, e);
6925     }
6926    
6927 });
6928
6929  
6930
6931  /*
6932  * - LGPL
6933  *
6934  * slider
6935  * 
6936  */
6937
6938
6939 /**
6940  * @class Roo.bootstrap.Slider
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap Slider class
6943  *    
6944  * @constructor
6945  * Create a new Slider
6946  * @param {Object} config The config object
6947  */
6948
6949 Roo.bootstrap.Slider = function(config){
6950     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 };
6952
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6954     
6955     getAutoCreate : function(){
6956         
6957         var cfg = {
6958             tag: 'div',
6959             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960             cn: [
6961                 {
6962                     tag: 'a',
6963                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6964                 }
6965             ]
6966         };
6967         
6968         return cfg;
6969     }
6970    
6971 });
6972
6973  /*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984
6985 /**
6986  * @class Roo.grid.ColumnModel
6987  * @extends Roo.util.Observable
6988  * This is the default implementation of a ColumnModel used by the Grid. It defines
6989  * the columns in the grid.
6990  * <br>Usage:<br>
6991  <pre><code>
6992  var colModel = new Roo.grid.ColumnModel([
6993         {header: "Ticker", width: 60, sortable: true, locked: true},
6994         {header: "Company Name", width: 150, sortable: true},
6995         {header: "Market Cap.", width: 100, sortable: true},
6996         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997         {header: "Employees", width: 100, sortable: true, resizable: false}
6998  ]);
6999  </code></pre>
7000  * <p>
7001  
7002  * The config options listed for this class are options which may appear in each
7003  * individual column definition.
7004  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005  * @constructor
7006  * @param {Object} config An Array of column config objects. See this class's
7007  * config objects for details.
7008 */
7009 Roo.grid.ColumnModel = function(config){
7010         /**
7011      * The config passed into the constructor
7012      */
7013     this.config = config;
7014     this.lookup = {};
7015
7016     // if no id, create one
7017     // if the column does not have a dataIndex mapping,
7018     // map it to the order it is in the config
7019     for(var i = 0, len = config.length; i < len; i++){
7020         var c = config[i];
7021         if(typeof c.dataIndex == "undefined"){
7022             c.dataIndex = i;
7023         }
7024         if(typeof c.renderer == "string"){
7025             c.renderer = Roo.util.Format[c.renderer];
7026         }
7027         if(typeof c.id == "undefined"){
7028             c.id = Roo.id();
7029         }
7030         if(c.editor && c.editor.xtype){
7031             c.editor  = Roo.factory(c.editor, Roo.grid);
7032         }
7033         if(c.editor && c.editor.isFormField){
7034             c.editor = new Roo.grid.GridEditor(c.editor);
7035         }
7036         this.lookup[c.id] = c;
7037     }
7038
7039     /**
7040      * The width of columns which have no width specified (defaults to 100)
7041      * @type Number
7042      */
7043     this.defaultWidth = 100;
7044
7045     /**
7046      * Default sortable of columns which have no sortable specified (defaults to false)
7047      * @type Boolean
7048      */
7049     this.defaultSortable = false;
7050
7051     this.addEvents({
7052         /**
7053              * @event widthchange
7054              * Fires when the width of a column changes.
7055              * @param {ColumnModel} this
7056              * @param {Number} columnIndex The column index
7057              * @param {Number} newWidth The new width
7058              */
7059             "widthchange": true,
7060         /**
7061              * @event headerchange
7062              * Fires when the text of a header changes.
7063              * @param {ColumnModel} this
7064              * @param {Number} columnIndex The column index
7065              * @param {Number} newText The new header text
7066              */
7067             "headerchange": true,
7068         /**
7069              * @event hiddenchange
7070              * Fires when a column is hidden or "unhidden".
7071              * @param {ColumnModel} this
7072              * @param {Number} columnIndex The column index
7073              * @param {Boolean} hidden true if hidden, false otherwise
7074              */
7075             "hiddenchange": true,
7076             /**
7077          * @event columnmoved
7078          * Fires when a column is moved.
7079          * @param {ColumnModel} this
7080          * @param {Number} oldIndex
7081          * @param {Number} newIndex
7082          */
7083         "columnmoved" : true,
7084         /**
7085          * @event columlockchange
7086          * Fires when a column's locked state is changed
7087          * @param {ColumnModel} this
7088          * @param {Number} colIndex
7089          * @param {Boolean} locked true if locked
7090          */
7091         "columnlockchange" : true
7092     });
7093     Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 };
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096     /**
7097      * @cfg {String} header The header text to display in the Grid view.
7098      */
7099     /**
7100      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102      * specified, the column's index is used as an index into the Record's data Array.
7103      */
7104     /**
7105      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107      */
7108     /**
7109      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110      * Defaults to the value of the {@link #defaultSortable} property.
7111      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112      */
7113     /**
7114      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121      */
7122     /**
7123      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124      */
7125     /**
7126      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130      */
7131        /**
7132      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7133      */
7134     /**
7135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} cursor (Optional)
7142      */
7143     /**
7144      * @cfg {String} tooltip (Optional)
7145      */
7146     /**
7147      * @cfg {Number} xs (Optional)
7148      */
7149     /**
7150      * @cfg {Number} sm (Optional)
7151      */
7152     /**
7153      * @cfg {Number} md (Optional)
7154      */
7155     /**
7156      * @cfg {Number} lg (Optional)
7157      */
7158     /**
7159      * Returns the id of the column at the specified index.
7160      * @param {Number} index The column index
7161      * @return {String} the id
7162      */
7163     getColumnId : function(index){
7164         return this.config[index].id;
7165     },
7166
7167     /**
7168      * Returns the column for a specified id.
7169      * @param {String} id The column id
7170      * @return {Object} the column
7171      */
7172     getColumnById : function(id){
7173         return this.lookup[id];
7174     },
7175
7176     
7177     /**
7178      * Returns the column for a specified dataIndex.
7179      * @param {String} dataIndex The column dataIndex
7180      * @return {Object|Boolean} the column or false if not found
7181      */
7182     getColumnByDataIndex: function(dataIndex){
7183         var index = this.findColumnIndex(dataIndex);
7184         return index > -1 ? this.config[index] : false;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column id.
7189      * @param {String} id The column id
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     getIndexById : function(id){
7193         for(var i = 0, len = this.config.length; i < len; i++){
7194             if(this.config[i].id == id){
7195                 return i;
7196             }
7197         }
7198         return -1;
7199     },
7200     
7201     /**
7202      * Returns the index for a specified column dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Number} the index, or -1 if not found
7205      */
7206     
7207     findColumnIndex : function(dataIndex){
7208         for(var i = 0, len = this.config.length; i < len; i++){
7209             if(this.config[i].dataIndex == dataIndex){
7210                 return i;
7211             }
7212         }
7213         return -1;
7214     },
7215     
7216     
7217     moveColumn : function(oldIndex, newIndex){
7218         var c = this.config[oldIndex];
7219         this.config.splice(oldIndex, 1);
7220         this.config.splice(newIndex, 0, c);
7221         this.dataMap = null;
7222         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223     },
7224
7225     isLocked : function(colIndex){
7226         return this.config[colIndex].locked === true;
7227     },
7228
7229     setLocked : function(colIndex, value, suppressEvent){
7230         if(this.isLocked(colIndex) == value){
7231             return;
7232         }
7233         this.config[colIndex].locked = value;
7234         if(!suppressEvent){
7235             this.fireEvent("columnlockchange", this, colIndex, value);
7236         }
7237     },
7238
7239     getTotalLockedWidth : function(){
7240         var totalWidth = 0;
7241         for(var i = 0; i < this.config.length; i++){
7242             if(this.isLocked(i) && !this.isHidden(i)){
7243                 this.totalWidth += this.getColumnWidth(i);
7244             }
7245         }
7246         return totalWidth;
7247     },
7248
7249     getLockedCount : function(){
7250         for(var i = 0, len = this.config.length; i < len; i++){
7251             if(!this.isLocked(i)){
7252                 return i;
7253             }
7254         }
7255         
7256         return this.config.length;
7257     },
7258
7259     /**
7260      * Returns the number of columns.
7261      * @return {Number}
7262      */
7263     getColumnCount : function(visibleOnly){
7264         if(visibleOnly === true){
7265             var c = 0;
7266             for(var i = 0, len = this.config.length; i < len; i++){
7267                 if(!this.isHidden(i)){
7268                     c++;
7269                 }
7270             }
7271             return c;
7272         }
7273         return this.config.length;
7274     },
7275
7276     /**
7277      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278      * @param {Function} fn
7279      * @param {Object} scope (optional)
7280      * @return {Array} result
7281      */
7282     getColumnsBy : function(fn, scope){
7283         var r = [];
7284         for(var i = 0, len = this.config.length; i < len; i++){
7285             var c = this.config[i];
7286             if(fn.call(scope||this, c, i) === true){
7287                 r[r.length] = c;
7288             }
7289         }
7290         return r;
7291     },
7292
7293     /**
7294      * Returns true if the specified column is sortable.
7295      * @param {Number} col The column index
7296      * @return {Boolean}
7297      */
7298     isSortable : function(col){
7299         if(typeof this.config[col].sortable == "undefined"){
7300             return this.defaultSortable;
7301         }
7302         return this.config[col].sortable;
7303     },
7304
7305     /**
7306      * Returns the rendering (formatting) function defined for the column.
7307      * @param {Number} col The column index.
7308      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309      */
7310     getRenderer : function(col){
7311         if(!this.config[col].renderer){
7312             return Roo.grid.ColumnModel.defaultRenderer;
7313         }
7314         return this.config[col].renderer;
7315     },
7316
7317     /**
7318      * Sets the rendering (formatting) function for a column.
7319      * @param {Number} col The column index
7320      * @param {Function} fn The function to use to process the cell's raw data
7321      * to return HTML markup for the grid view. The render function is called with
7322      * the following parameters:<ul>
7323      * <li>Data value.</li>
7324      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325      * <li>css A CSS style string to apply to the table cell.</li>
7326      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328      * <li>Row index</li>
7329      * <li>Column index</li>
7330      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331      */
7332     setRenderer : function(col, fn){
7333         this.config[col].renderer = fn;
7334     },
7335
7336     /**
7337      * Returns the width for the specified column.
7338      * @param {Number} col The column index
7339      * @return {Number}
7340      */
7341     getColumnWidth : function(col){
7342         return this.config[col].width * 1 || this.defaultWidth;
7343     },
7344
7345     /**
7346      * Sets the width for a column.
7347      * @param {Number} col The column index
7348      * @param {Number} width The new width
7349      */
7350     setColumnWidth : function(col, width, suppressEvent){
7351         this.config[col].width = width;
7352         this.totalWidth = null;
7353         if(!suppressEvent){
7354              this.fireEvent("widthchange", this, col, width);
7355         }
7356     },
7357
7358     /**
7359      * Returns the total width of all columns.
7360      * @param {Boolean} includeHidden True to include hidden column widths
7361      * @return {Number}
7362      */
7363     getTotalWidth : function(includeHidden){
7364         if(!this.totalWidth){
7365             this.totalWidth = 0;
7366             for(var i = 0, len = this.config.length; i < len; i++){
7367                 if(includeHidden || !this.isHidden(i)){
7368                     this.totalWidth += this.getColumnWidth(i);
7369                 }
7370             }
7371         }
7372         return this.totalWidth;
7373     },
7374
7375     /**
7376      * Returns the header for the specified column.
7377      * @param {Number} col The column index
7378      * @return {String}
7379      */
7380     getColumnHeader : function(col){
7381         return this.config[col].header;
7382     },
7383
7384     /**
7385      * Sets the header for a column.
7386      * @param {Number} col The column index
7387      * @param {String} header The new header
7388      */
7389     setColumnHeader : function(col, header){
7390         this.config[col].header = header;
7391         this.fireEvent("headerchange", this, col, header);
7392     },
7393
7394     /**
7395      * Returns the tooltip for the specified column.
7396      * @param {Number} col The column index
7397      * @return {String}
7398      */
7399     getColumnTooltip : function(col){
7400             return this.config[col].tooltip;
7401     },
7402     /**
7403      * Sets the tooltip for a column.
7404      * @param {Number} col The column index
7405      * @param {String} tooltip The new tooltip
7406      */
7407     setColumnTooltip : function(col, tooltip){
7408             this.config[col].tooltip = tooltip;
7409     },
7410
7411     /**
7412      * Returns the dataIndex for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getDataIndex : function(col){
7417         return this.config[col].dataIndex;
7418     },
7419
7420     /**
7421      * Sets the dataIndex for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} dataIndex The new dataIndex
7424      */
7425     setDataIndex : function(col, dataIndex){
7426         this.config[col].dataIndex = dataIndex;
7427     },
7428
7429     
7430     
7431     /**
7432      * Returns true if the cell is editable.
7433      * @param {Number} colIndex The column index
7434      * @param {Number} rowIndex The row index - this is nto actually used..?
7435      * @return {Boolean}
7436      */
7437     isCellEditable : function(colIndex, rowIndex){
7438         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439     },
7440
7441     /**
7442      * Returns the editor defined for the cell/column.
7443      * return false or null to disable editing.
7444      * @param {Number} colIndex The column index
7445      * @param {Number} rowIndex The row index
7446      * @return {Object}
7447      */
7448     getCellEditor : function(colIndex, rowIndex){
7449         return this.config[colIndex].editor;
7450     },
7451
7452     /**
7453      * Sets if a column is editable.
7454      * @param {Number} col The column index
7455      * @param {Boolean} editable True if the column is editable
7456      */
7457     setEditable : function(col, editable){
7458         this.config[col].editable = editable;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column is hidden.
7464      * @param {Number} colIndex The column index
7465      * @return {Boolean}
7466      */
7467     isHidden : function(colIndex){
7468         return this.config[colIndex].hidden;
7469     },
7470
7471
7472     /**
7473      * Returns true if the column width cannot be changed
7474      */
7475     isFixed : function(colIndex){
7476         return this.config[colIndex].fixed;
7477     },
7478
7479     /**
7480      * Returns true if the column can be resized
7481      * @return {Boolean}
7482      */
7483     isResizable : function(colIndex){
7484         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485     },
7486     /**
7487      * Sets if a column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @param {Boolean} hidden True if the column is hidden
7490      */
7491     setHidden : function(colIndex, hidden){
7492         this.config[colIndex].hidden = hidden;
7493         this.totalWidth = null;
7494         this.fireEvent("hiddenchange", this, colIndex, hidden);
7495     },
7496
7497     /**
7498      * Sets the editor for a column.
7499      * @param {Number} col The column index
7500      * @param {Object} editor The editor object
7501      */
7502     setEditor : function(col, editor){
7503         this.config[col].editor = editor;
7504     }
7505 });
7506
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 {
7509     if(typeof value == "object") {
7510         return value;
7511     }
7512         if(typeof value == "string" && value.length < 1){
7513             return "&#160;";
7514         }
7515     
7516         return String.format("{0}", value);
7517 };
7518
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 /*
7522  * Based on:
7523  * Ext JS Library 1.1.1
7524  * Copyright(c) 2006-2007, Ext JS, LLC.
7525  *
7526  * Originally Released Under LGPL - original licence link has changed is not relivant.
7527  *
7528  * Fork - LGPL
7529  * <script type="text/javascript">
7530  */
7531  
7532 /**
7533  * @class Roo.LoadMask
7534  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7535  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7537  * element's UpdateManager load indicator and will be destroyed after the initial load.
7538  * @constructor
7539  * Create a new LoadMask
7540  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541  * @param {Object} config The config object
7542  */
7543 Roo.LoadMask = function(el, config){
7544     this.el = Roo.get(el);
7545     Roo.apply(this, config);
7546     if(this.store){
7547         this.store.on('beforeload', this.onBeforeLoad, this);
7548         this.store.on('load', this.onLoad, this);
7549         this.store.on('loadexception', this.onLoadException, this);
7550         this.removeMask = false;
7551     }else{
7552         var um = this.el.getUpdateManager();
7553         um.showLoadIndicator = false; // disable the default indicator
7554         um.on('beforeupdate', this.onBeforeLoad, this);
7555         um.on('update', this.onLoad, this);
7556         um.on('failure', this.onLoad, this);
7557         this.removeMask = true;
7558     }
7559 };
7560
7561 Roo.LoadMask.prototype = {
7562     /**
7563      * @cfg {Boolean} removeMask
7564      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7566      */
7567     /**
7568      * @cfg {String} msg
7569      * The text to display in a centered loading message box (defaults to 'Loading...')
7570      */
7571     msg : 'Loading...',
7572     /**
7573      * @cfg {String} msgCls
7574      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575      */
7576     msgCls : 'x-mask-loading',
7577
7578     /**
7579      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580      * @type Boolean
7581      */
7582     disabled: false,
7583
7584     /**
7585      * Disables the mask to prevent it from being displayed
7586      */
7587     disable : function(){
7588        this.disabled = true;
7589     },
7590
7591     /**
7592      * Enables the mask so that it can be displayed
7593      */
7594     enable : function(){
7595         this.disabled = false;
7596     },
7597     
7598     onLoadException : function()
7599     {
7600         Roo.log(arguments);
7601         
7602         if (typeof(arguments[3]) != 'undefined') {
7603             Roo.MessageBox.alert("Error loading",arguments[3]);
7604         } 
7605         /*
7606         try {
7607             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7609             }   
7610         } catch(e) {
7611             
7612         }
7613         */
7614     
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617     // private
7618     onLoad : function()
7619     {
7620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621     },
7622
7623     // private
7624     onBeforeLoad : function(){
7625         if(!this.disabled){
7626             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7627         }
7628     },
7629
7630     // private
7631     destroy : function(){
7632         if(this.store){
7633             this.store.un('beforeload', this.onBeforeLoad, this);
7634             this.store.un('load', this.onLoad, this);
7635             this.store.un('loadexception', this.onLoadException, this);
7636         }else{
7637             var um = this.el.getUpdateManager();
7638             um.un('beforeupdate', this.onBeforeLoad, this);
7639             um.un('update', this.onLoad, this);
7640             um.un('failure', this.onLoad, this);
7641         }
7642     }
7643 };/*
7644  * - LGPL
7645  *
7646  * table
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.Table
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap Table class
7654  * @cfg {String} cls table class
7655  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656  * @cfg {String} bgcolor Specifies the background color for a table
7657  * @cfg {Number} border Specifies whether the table cells should have borders or not
7658  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659  * @cfg {Number} cellspacing Specifies the space between cells
7660  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662  * @cfg {String} sortable Specifies that the table should be sortable
7663  * @cfg {String} summary Specifies a summary of the content of a table
7664  * @cfg {Number} width Specifies the width of a table
7665  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666  * 
7667  * @cfg {boolean} striped Should the rows be alternative striped
7668  * @cfg {boolean} bordered Add borders to the table
7669  * @cfg {boolean} hover Add hover highlighting
7670  * @cfg {boolean} condensed Format condensed
7671  * @cfg {boolean} responsive Format condensed
7672  * @cfg {Boolean} loadMask (true|false) default false
7673  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675  * @cfg {Boolean} rowSelection (true|false) default false
7676  * @cfg {Boolean} cellSelection (true|false) default false
7677  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7679  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7680  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7681  
7682  * 
7683  * @constructor
7684  * Create a new Table
7685  * @param {Object} config The config object
7686  */
7687
7688 Roo.bootstrap.Table = function(config){
7689     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7690     
7691   
7692     
7693     // BC...
7694     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698     
7699     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700     if (this.sm) {
7701         this.sm.grid = this;
7702         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703         this.sm = this.selModel;
7704         this.sm.xmodule = this.xmodule || false;
7705     }
7706     
7707     if (this.cm && typeof(this.cm.config) == 'undefined') {
7708         this.colModel = new Roo.grid.ColumnModel(this.cm);
7709         this.cm = this.colModel;
7710         this.cm.xmodule = this.xmodule || false;
7711     }
7712     if (this.store) {
7713         this.store= Roo.factory(this.store, Roo.data);
7714         this.ds = this.store;
7715         this.ds.xmodule = this.xmodule || false;
7716          
7717     }
7718     if (this.footer && this.store) {
7719         this.footer.dataSource = this.ds;
7720         this.footer = Roo.factory(this.footer);
7721     }
7722     
7723     /** @private */
7724     this.addEvents({
7725         /**
7726          * @event cellclick
7727          * Fires when a cell is clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "cellclick" : true,
7735         /**
7736          * @event celldblclick
7737          * Fires when a cell is double clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Number} columnIndex
7742          * @param {Roo.EventObject} e
7743          */
7744         "celldblclick" : true,
7745         /**
7746          * @event rowclick
7747          * Fires when a row is clicked
7748          * @param {Roo.bootstrap.Table} this
7749          * @param {Roo.Element} el
7750          * @param {Number} rowIndex
7751          * @param {Roo.EventObject} e
7752          */
7753         "rowclick" : true,
7754         /**
7755          * @event rowdblclick
7756          * Fires when a row is double clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "rowdblclick" : true,
7763         /**
7764          * @event mouseover
7765          * Fires when a mouseover occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseover" : true,
7773         /**
7774          * @event mouseout
7775          * Fires when a mouseout occur
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Roo.Element} el
7778          * @param {Number} rowIndex
7779          * @param {Number} columnIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "mouseout" : true,
7783         /**
7784          * @event rowclass
7785          * Fires when a row is rendered, so you can change add a style to it.
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7788          */
7789         'rowclass' : true,
7790           /**
7791          * @event rowsrendered
7792          * Fires when all the  rows have been rendered
7793          * @param {Roo.bootstrap.Table} this
7794          */
7795         'rowsrendered' : true,
7796         /**
7797          * @event contextmenu
7798          * The raw contextmenu event for the entire grid.
7799          * @param {Roo.EventObject} e
7800          */
7801         "contextmenu" : true,
7802         /**
7803          * @event rowcontextmenu
7804          * Fires when a row is right clicked
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Number} rowIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "rowcontextmenu" : true,
7810         /**
7811          * @event cellcontextmenu
7812          * Fires when a cell is right clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Number} rowIndex
7815          * @param {Number} cellIndex
7816          * @param {Roo.EventObject} e
7817          */
7818          "cellcontextmenu" : true,
7819          /**
7820          * @event headercontextmenu
7821          * Fires when a header is right clicked
7822          * @param {Roo.bootstrap.Table} this
7823          * @param {Number} columnIndex
7824          * @param {Roo.EventObject} e
7825          */
7826         "headercontextmenu" : true
7827     });
7828 };
7829
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7831     
7832     cls: false,
7833     align: false,
7834     bgcolor: false,
7835     border: false,
7836     cellpadding: false,
7837     cellspacing: false,
7838     frame: false,
7839     rules: false,
7840     sortable: false,
7841     summary: false,
7842     width: false,
7843     striped : false,
7844     scrollBody : false,
7845     bordered: false,
7846     hover:  false,
7847     condensed : false,
7848     responsive : false,
7849     sm : false,
7850     cm : false,
7851     store : false,
7852     loadMask : false,
7853     footerShow : true,
7854     headerShow : true,
7855   
7856     rowSelection : false,
7857     cellSelection : false,
7858     layout : false,
7859     
7860     // Roo.Element - the tbody
7861     mainBody: false,
7862     // Roo.Element - thead element
7863     mainHead: false,
7864     
7865     container: false, // used by gridpanel...
7866     
7867     lazyLoad : false,
7868     
7869     CSS : Roo.util.CSS,
7870     
7871     auto_hide_footer : false,
7872     
7873     getAutoCreate : function()
7874     {
7875         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7876         
7877         cfg = {
7878             tag: 'table',
7879             cls : 'table',
7880             cn : []
7881         };
7882         if (this.scrollBody) {
7883             cfg.cls += ' table-body-fixed';
7884         }    
7885         if (this.striped) {
7886             cfg.cls += ' table-striped';
7887         }
7888         
7889         if (this.hover) {
7890             cfg.cls += ' table-hover';
7891         }
7892         if (this.bordered) {
7893             cfg.cls += ' table-bordered';
7894         }
7895         if (this.condensed) {
7896             cfg.cls += ' table-condensed';
7897         }
7898         if (this.responsive) {
7899             cfg.cls += ' table-responsive';
7900         }
7901         
7902         if (this.cls) {
7903             cfg.cls+=  ' ' +this.cls;
7904         }
7905         
7906         // this lot should be simplifed...
7907         var _t = this;
7908         var cp = [
7909             'align',
7910             'bgcolor',
7911             'border',
7912             'cellpadding',
7913             'cellspacing',
7914             'frame',
7915             'rules',
7916             'sortable',
7917             'summary',
7918             'width'
7919         ].forEach(function(k) {
7920             if (_t[k]) {
7921                 cfg[k] = _t[k];
7922             }
7923         });
7924         
7925         
7926         if (this.layout) {
7927             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928         }
7929         
7930         if(this.store || this.cm){
7931             if(this.headerShow){
7932                 cfg.cn.push(this.renderHeader());
7933             }
7934             
7935             cfg.cn.push(this.renderBody());
7936             
7937             if(this.footerShow){
7938                 cfg.cn.push(this.renderFooter());
7939             }
7940             // where does this come from?
7941             //cfg.cls+=  ' TableGrid';
7942         }
7943         
7944         return { cn : [ cfg ] };
7945     },
7946     
7947     initEvents : function()
7948     {   
7949         if(!this.store || !this.cm){
7950             return;
7951         }
7952         if (this.selModel) {
7953             this.selModel.initEvents();
7954         }
7955         
7956         
7957         //Roo.log('initEvents with ds!!!!');
7958         
7959         this.mainBody = this.el.select('tbody', true).first();
7960         this.mainHead = this.el.select('thead', true).first();
7961         this.mainFoot = this.el.select('tfoot', true).first();
7962         
7963         
7964         
7965         var _this = this;
7966         
7967         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968             e.on('click', _this.sort, _this);
7969         });
7970         
7971         this.mainBody.on("click", this.onClick, this);
7972         this.mainBody.on("dblclick", this.onDblClick, this);
7973         
7974         // why is this done????? = it breaks dialogs??
7975         //this.parent().el.setStyle('position', 'relative');
7976         
7977         
7978         if (this.footer) {
7979             this.footer.parentId = this.id;
7980             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981             
7982             if(this.lazyLoad){
7983                 this.el.select('tfoot tr td').first().addClass('hide');
7984             }
7985         } 
7986         
7987         if(this.loadMask) {
7988             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989         }
7990         
7991         this.store.on('load', this.onLoad, this);
7992         this.store.on('beforeload', this.onBeforeLoad, this);
7993         this.store.on('update', this.onUpdate, this);
7994         this.store.on('add', this.onAdd, this);
7995         this.store.on("clear", this.clear, this);
7996         
7997         this.el.on("contextmenu", this.onContextMenu, this);
7998         
7999         this.mainBody.on('scroll', this.onBodyScroll, this);
8000         
8001         this.cm.on("headerchange", this.onHeaderChange, this);
8002         
8003         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004         
8005     },
8006     
8007     onContextMenu : function(e, t)
8008     {
8009         this.processEvent("contextmenu", e);
8010     },
8011     
8012     processEvent : function(name, e)
8013     {
8014         if (name != 'touchstart' ) {
8015             this.fireEvent(name, e);    
8016         }
8017         
8018         var t = e.getTarget();
8019         
8020         var cell = Roo.get(t);
8021         
8022         if(!cell){
8023             return;
8024         }
8025         
8026         if(cell.findParent('tfoot', false, true)){
8027             return;
8028         }
8029         
8030         if(cell.findParent('thead', false, true)){
8031             
8032             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033                 cell = Roo.get(t).findParent('th', false, true);
8034                 if (!cell) {
8035                     Roo.log("failed to find th in thead?");
8036                     Roo.log(e.getTarget());
8037                     return;
8038                 }
8039             }
8040             
8041             var cellIndex = cell.dom.cellIndex;
8042             
8043             var ename = name == 'touchstart' ? 'click' : name;
8044             this.fireEvent("header" + ename, this, cellIndex, e);
8045             
8046             return;
8047         }
8048         
8049         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050             cell = Roo.get(t).findParent('td', false, true);
8051             if (!cell) {
8052                 Roo.log("failed to find th in tbody?");
8053                 Roo.log(e.getTarget());
8054                 return;
8055             }
8056         }
8057         
8058         var row = cell.findParent('tr', false, true);
8059         var cellIndex = cell.dom.cellIndex;
8060         var rowIndex = row.dom.rowIndex - 1;
8061         
8062         if(row !== false){
8063             
8064             this.fireEvent("row" + name, this, rowIndex, e);
8065             
8066             if(cell !== false){
8067             
8068                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069             }
8070         }
8071         
8072     },
8073     
8074     onMouseover : function(e, el)
8075     {
8076         var cell = Roo.get(el);
8077         
8078         if(!cell){
8079             return;
8080         }
8081         
8082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083             cell = cell.findParent('td', false, true);
8084         }
8085         
8086         var row = cell.findParent('tr', false, true);
8087         var cellIndex = cell.dom.cellIndex;
8088         var rowIndex = row.dom.rowIndex - 1; // start from 0
8089         
8090         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091         
8092     },
8093     
8094     onMouseout : function(e, el)
8095     {
8096         var cell = Roo.get(el);
8097         
8098         if(!cell){
8099             return;
8100         }
8101         
8102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103             cell = cell.findParent('td', false, true);
8104         }
8105         
8106         var row = cell.findParent('tr', false, true);
8107         var cellIndex = cell.dom.cellIndex;
8108         var rowIndex = row.dom.rowIndex - 1; // start from 0
8109         
8110         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111         
8112     },
8113     
8114     onClick : function(e, el)
8115     {
8116         var cell = Roo.get(el);
8117         
8118         if(!cell || (!this.cellSelection && !this.rowSelection)){
8119             return;
8120         }
8121         
8122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123             cell = cell.findParent('td', false, true);
8124         }
8125         
8126         if(!cell || typeof(cell) == 'undefined'){
8127             return;
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         
8132         if(!row || typeof(row) == 'undefined'){
8133             return;
8134         }
8135         
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = this.getRowIndex(row);
8138         
8139         // why??? - should these not be based on SelectionModel?
8140         if(this.cellSelection){
8141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142         }
8143         
8144         if(this.rowSelection){
8145             this.fireEvent('rowclick', this, row, rowIndex, e);
8146         }
8147         
8148         
8149     },
8150         
8151     onDblClick : function(e,el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell || (!this.cellSelection && !this.rowSelection)){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         if(!cell || typeof(cell) == 'undefined'){
8164             return;
8165         }
8166         
8167         var row = cell.findParent('tr', false, true);
8168         
8169         if(!row || typeof(row) == 'undefined'){
8170             return;
8171         }
8172         
8173         var cellIndex = cell.dom.cellIndex;
8174         var rowIndex = this.getRowIndex(row);
8175         
8176         if(this.cellSelection){
8177             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178         }
8179         
8180         if(this.rowSelection){
8181             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182         }
8183     },
8184     
8185     sort : function(e,el)
8186     {
8187         var col = Roo.get(el);
8188         
8189         if(!col.hasClass('sortable')){
8190             return;
8191         }
8192         
8193         var sort = col.attr('sort');
8194         var dir = 'ASC';
8195         
8196         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197             dir = 'DESC';
8198         }
8199         
8200         this.store.sortInfo = {field : sort, direction : dir};
8201         
8202         if (this.footer) {
8203             Roo.log("calling footer first");
8204             this.footer.onClick('first');
8205         } else {
8206         
8207             this.store.load({ params : { start : 0 } });
8208         }
8209     },
8210     
8211     renderHeader : function()
8212     {
8213         var header = {
8214             tag: 'thead',
8215             cn : []
8216         };
8217         
8218         var cm = this.cm;
8219         this.totalWidth = 0;
8220         
8221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222             
8223             var config = cm.config[i];
8224             
8225             var c = {
8226                 tag: 'th',
8227                 cls : 'x-hcol-' + i,
8228                 style : '',
8229                 html: cm.getColumnHeader(i)
8230             };
8231             
8232             var hh = '';
8233             
8234             if(typeof(config.sortable) != 'undefined' && config.sortable){
8235                 c.cls = 'sortable';
8236                 c.html = '<i class="glyphicon"></i>' + c.html;
8237             }
8238             
8239             // could use BS4 hidden-..-down 
8240             
8241             if(typeof(config.lgHeader) != 'undefined'){
8242                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243             }
8244             
8245             if(typeof(config.mdHeader) != 'undefined'){
8246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247             }
8248             
8249             if(typeof(config.smHeader) != 'undefined'){
8250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251             }
8252             
8253             if(typeof(config.xsHeader) != 'undefined'){
8254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8255             }
8256             
8257             if(hh.length){
8258                 c.html = hh;
8259             }
8260             
8261             if(typeof(config.tooltip) != 'undefined'){
8262                 c.tooltip = config.tooltip;
8263             }
8264             
8265             if(typeof(config.colspan) != 'undefined'){
8266                 c.colspan = config.colspan;
8267             }
8268             
8269             if(typeof(config.hidden) != 'undefined' && config.hidden){
8270                 c.style += ' display:none;';
8271             }
8272             
8273             if(typeof(config.dataIndex) != 'undefined'){
8274                 c.sort = config.dataIndex;
8275             }
8276             
8277            
8278             
8279             if(typeof(config.align) != 'undefined' && config.align.length){
8280                 c.style += ' text-align:' + config.align + ';';
8281             }
8282             
8283             if(typeof(config.width) != 'undefined'){
8284                 c.style += ' width:' + config.width + 'px;';
8285                 this.totalWidth += config.width;
8286             } else {
8287                 this.totalWidth += 100; // assume minimum of 100 per column?
8288             }
8289             
8290             if(typeof(config.cls) != 'undefined'){
8291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292             }
8293             
8294             ['xs','sm','md','lg'].map(function(size){
8295                 
8296                 if(typeof(config[size]) == 'undefined'){
8297                     return;
8298                 }
8299                  
8300                 if (!config[size]) { // 0 = hidden
8301                     // BS 4 '0' is treated as hide that column and below.
8302                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303                     return;
8304                 }
8305                 
8306                 c.cls += ' col-' + size + '-' + config[size] + (
8307                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8308                 );
8309                 
8310                 
8311             });
8312             
8313             header.cn.push(c)
8314         }
8315         
8316         return header;
8317     },
8318     
8319     renderBody : function()
8320     {
8321         var body = {
8322             tag: 'tbody',
8323             cn : [
8324                 {
8325                     tag: 'tr',
8326                     cn : [
8327                         {
8328                             tag : 'td',
8329                             colspan :  this.cm.getColumnCount()
8330                         }
8331                     ]
8332                 }
8333             ]
8334         };
8335         
8336         return body;
8337     },
8338     
8339     renderFooter : function()
8340     {
8341         var footer = {
8342             tag: 'tfoot',
8343             cn : [
8344                 {
8345                     tag: 'tr',
8346                     cn : [
8347                         {
8348                             tag : 'td',
8349                             colspan :  this.cm.getColumnCount()
8350                         }
8351                     ]
8352                 }
8353             ]
8354         };
8355         
8356         return footer;
8357     },
8358     
8359     
8360     
8361     onLoad : function()
8362     {
8363 //        Roo.log('ds onload');
8364         this.clear();
8365         
8366         var _this = this;
8367         var cm = this.cm;
8368         var ds = this.store;
8369         
8370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372             if (_this.store.sortInfo) {
8373                     
8374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8376                 }
8377                 
8378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8380                 }
8381             }
8382         });
8383         
8384         var tbody =  this.mainBody;
8385               
8386         if(ds.getCount() > 0){
8387             ds.data.each(function(d,rowIndex){
8388                 var row =  this.renderRow(cm, ds, rowIndex);
8389                 
8390                 tbody.createChild(row);
8391                 
8392                 var _this = this;
8393                 
8394                 if(row.cellObjects.length){
8395                     Roo.each(row.cellObjects, function(r){
8396                         _this.renderCellObject(r);
8397                     })
8398                 }
8399                 
8400             }, this);
8401         }
8402         
8403         var tfoot = this.el.select('tfoot', true).first();
8404         
8405         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406             
8407             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408             
8409             var total = this.ds.getTotalCount();
8410             
8411             if(this.footer.pageSize < total){
8412                 this.mainFoot.show();
8413             }
8414         }
8415         
8416         Roo.each(this.el.select('tbody td', true).elements, function(e){
8417             e.on('mouseover', _this.onMouseover, _this);
8418         });
8419         
8420         Roo.each(this.el.select('tbody td', true).elements, function(e){
8421             e.on('mouseout', _this.onMouseout, _this);
8422         });
8423         this.fireEvent('rowsrendered', this);
8424         
8425         this.autoSize();
8426     },
8427     
8428     
8429     onUpdate : function(ds,record)
8430     {
8431         this.refreshRow(record);
8432         this.autoSize();
8433     },
8434     
8435     onRemove : function(ds, record, index, isUpdate){
8436         if(isUpdate !== true){
8437             this.fireEvent("beforerowremoved", this, index, record);
8438         }
8439         var bt = this.mainBody.dom;
8440         
8441         var rows = this.el.select('tbody > tr', true).elements;
8442         
8443         if(typeof(rows[index]) != 'undefined'){
8444             bt.removeChild(rows[index].dom);
8445         }
8446         
8447 //        if(bt.rows[index]){
8448 //            bt.removeChild(bt.rows[index]);
8449 //        }
8450         
8451         if(isUpdate !== true){
8452             //this.stripeRows(index);
8453             //this.syncRowHeights(index, index);
8454             //this.layout();
8455             this.fireEvent("rowremoved", this, index, record);
8456         }
8457     },
8458     
8459     onAdd : function(ds, records, rowIndex)
8460     {
8461         //Roo.log('on Add called');
8462         // - note this does not handle multiple adding very well..
8463         var bt = this.mainBody.dom;
8464         for (var i =0 ; i < records.length;i++) {
8465             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466             //Roo.log(records[i]);
8467             //Roo.log(this.store.getAt(rowIndex+i));
8468             this.insertRow(this.store, rowIndex + i, false);
8469             return;
8470         }
8471         
8472     },
8473     
8474     
8475     refreshRow : function(record){
8476         var ds = this.store, index;
8477         if(typeof record == 'number'){
8478             index = record;
8479             record = ds.getAt(index);
8480         }else{
8481             index = ds.indexOf(record);
8482             if (index < 0) {
8483                 return; // should not happen - but seems to 
8484             }
8485         }
8486         this.insertRow(ds, index, true);
8487         this.autoSize();
8488         this.onRemove(ds, record, index+1, true);
8489         this.autoSize();
8490         //this.syncRowHeights(index, index);
8491         //this.layout();
8492         this.fireEvent("rowupdated", this, index, record);
8493     },
8494     
8495     insertRow : function(dm, rowIndex, isUpdate){
8496         
8497         if(!isUpdate){
8498             this.fireEvent("beforerowsinserted", this, rowIndex);
8499         }
8500             //var s = this.getScrollState();
8501         var row = this.renderRow(this.cm, this.store, rowIndex);
8502         // insert before rowIndex..
8503         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504         
8505         var _this = this;
8506                 
8507         if(row.cellObjects.length){
8508             Roo.each(row.cellObjects, function(r){
8509                 _this.renderCellObject(r);
8510             })
8511         }
8512             
8513         if(!isUpdate){
8514             this.fireEvent("rowsinserted", this, rowIndex);
8515             //this.syncRowHeights(firstRow, lastRow);
8516             //this.stripeRows(firstRow);
8517             //this.layout();
8518         }
8519         
8520     },
8521     
8522     
8523     getRowDom : function(rowIndex)
8524     {
8525         var rows = this.el.select('tbody > tr', true).elements;
8526         
8527         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528         
8529     },
8530     // returns the object tree for a tr..
8531   
8532     
8533     renderRow : function(cm, ds, rowIndex) 
8534     {
8535         var d = ds.getAt(rowIndex);
8536         
8537         var row = {
8538             tag : 'tr',
8539             cls : 'x-row-' + rowIndex,
8540             cn : []
8541         };
8542             
8543         var cellObjects = [];
8544         
8545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546             var config = cm.config[i];
8547             
8548             var renderer = cm.getRenderer(i);
8549             var value = '';
8550             var id = false;
8551             
8552             if(typeof(renderer) !== 'undefined'){
8553                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554             }
8555             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556             // and are rendered into the cells after the row is rendered - using the id for the element.
8557             
8558             if(typeof(value) === 'object'){
8559                 id = Roo.id();
8560                 cellObjects.push({
8561                     container : id,
8562                     cfg : value 
8563                 })
8564             }
8565             
8566             var rowcfg = {
8567                 record: d,
8568                 rowIndex : rowIndex,
8569                 colIndex : i,
8570                 rowClass : ''
8571             };
8572
8573             this.fireEvent('rowclass', this, rowcfg);
8574             
8575             var td = {
8576                 tag: 'td',
8577                 cls : rowcfg.rowClass + ' x-col-' + i,
8578                 style: '',
8579                 html: (typeof(value) === 'object') ? '' : value
8580             };
8581             
8582             if (id) {
8583                 td.id = id;
8584             }
8585             
8586             if(typeof(config.colspan) != 'undefined'){
8587                 td.colspan = config.colspan;
8588             }
8589             
8590             if(typeof(config.hidden) != 'undefined' && config.hidden){
8591                 td.style += ' display:none;';
8592             }
8593             
8594             if(typeof(config.align) != 'undefined' && config.align.length){
8595                 td.style += ' text-align:' + config.align + ';';
8596             }
8597             if(typeof(config.valign) != 'undefined' && config.valign.length){
8598                 td.style += ' vertical-align:' + config.valign + ';';
8599             }
8600             
8601             if(typeof(config.width) != 'undefined'){
8602                 td.style += ' width:' +  config.width + 'px;';
8603             }
8604             
8605             if(typeof(config.cursor) != 'undefined'){
8606                 td.style += ' cursor:' +  config.cursor + ';';
8607             }
8608             
8609             if(typeof(config.cls) != 'undefined'){
8610                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611             }
8612             
8613             ['xs','sm','md','lg'].map(function(size){
8614                 
8615                 if(typeof(config[size]) == 'undefined'){
8616                     return;
8617                 }
8618                 
8619                 
8620                   
8621                 if (!config[size]) { // 0 = hidden
8622                     // BS 4 '0' is treated as hide that column and below.
8623                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624                     return;
8625                 }
8626                 
8627                 td.cls += ' col-' + size + '-' + config[size] + (
8628                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8629                 );
8630                  
8631
8632             });
8633             
8634             row.cn.push(td);
8635            
8636         }
8637         
8638         row.cellObjects = cellObjects;
8639         
8640         return row;
8641           
8642     },
8643     
8644     
8645     
8646     onBeforeLoad : function()
8647     {
8648         
8649     },
8650      /**
8651      * Remove all rows
8652      */
8653     clear : function()
8654     {
8655         this.el.select('tbody', true).first().dom.innerHTML = '';
8656     },
8657     /**
8658      * Show or hide a row.
8659      * @param {Number} rowIndex to show or hide
8660      * @param {Boolean} state hide
8661      */
8662     setRowVisibility : function(rowIndex, state)
8663     {
8664         var bt = this.mainBody.dom;
8665         
8666         var rows = this.el.select('tbody > tr', true).elements;
8667         
8668         if(typeof(rows[rowIndex]) == 'undefined'){
8669             return;
8670         }
8671         rows[rowIndex].dom.style.display = state ? '' : 'none';
8672     },
8673     
8674     
8675     getSelectionModel : function(){
8676         if(!this.selModel){
8677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678         }
8679         return this.selModel;
8680     },
8681     /*
8682      * Render the Roo.bootstrap object from renderder
8683      */
8684     renderCellObject : function(r)
8685     {
8686         var _this = this;
8687         
8688         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689         
8690         var t = r.cfg.render(r.container);
8691         
8692         if(r.cfg.cn){
8693             Roo.each(r.cfg.cn, function(c){
8694                 var child = {
8695                     container: t.getChildContainer(),
8696                     cfg: c
8697                 };
8698                 _this.renderCellObject(child);
8699             })
8700         }
8701     },
8702     
8703     getRowIndex : function(row)
8704     {
8705         var rowIndex = -1;
8706         
8707         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8708             if(el != row){
8709                 return;
8710             }
8711             
8712             rowIndex = index;
8713         });
8714         
8715         return rowIndex;
8716     },
8717      /**
8718      * Returns the grid's underlying element = used by panel.Grid
8719      * @return {Element} The element
8720      */
8721     getGridEl : function(){
8722         return this.el;
8723     },
8724      /**
8725      * Forces a resize - used by panel.Grid
8726      * @return {Element} The element
8727      */
8728     autoSize : function()
8729     {
8730         //var ctr = Roo.get(this.container.dom.parentElement);
8731         var ctr = Roo.get(this.el.dom);
8732         
8733         var thd = this.getGridEl().select('thead',true).first();
8734         var tbd = this.getGridEl().select('tbody', true).first();
8735         var tfd = this.getGridEl().select('tfoot', true).first();
8736         
8737         var cw = ctr.getWidth();
8738         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8739         
8740         if (tbd) {
8741             
8742             tbd.setWidth(ctr.getWidth());
8743             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744             // this needs fixing for various usage - currently only hydra job advers I think..
8745             //tdb.setHeight(
8746             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747             //); 
8748             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749             cw -= barsize;
8750         }
8751         cw = Math.max(cw, this.totalWidth);
8752         this.getGridEl().select('tbody tr',true).setWidth(cw);
8753         
8754         // resize 'expandable coloumn?
8755         
8756         return; // we doe not have a view in this design..
8757         
8758     },
8759     onBodyScroll: function()
8760     {
8761         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762         if(this.mainHead){
8763             this.mainHead.setStyle({
8764                 'position' : 'relative',
8765                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766             });
8767         }
8768         
8769         if(this.lazyLoad){
8770             
8771             var scrollHeight = this.mainBody.dom.scrollHeight;
8772             
8773             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774             
8775             var height = this.mainBody.getHeight();
8776             
8777             if(scrollHeight - height == scrollTop) {
8778                 
8779                 var total = this.ds.getTotalCount();
8780                 
8781                 if(this.footer.cursor + this.footer.pageSize < total){
8782                     
8783                     this.footer.ds.load({
8784                         params : {
8785                             start : this.footer.cursor + this.footer.pageSize,
8786                             limit : this.footer.pageSize
8787                         },
8788                         add : true
8789                     });
8790                 }
8791             }
8792             
8793         }
8794     },
8795     
8796     onHeaderChange : function()
8797     {
8798         var header = this.renderHeader();
8799         var table = this.el.select('table', true).first();
8800         
8801         this.mainHead.remove();
8802         this.mainHead = table.createChild(header, this.mainBody, false);
8803     },
8804     
8805     onHiddenChange : function(colModel, colIndex, hidden)
8806     {
8807         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809         
8810         this.CSS.updateRule(thSelector, "display", "");
8811         this.CSS.updateRule(tdSelector, "display", "");
8812         
8813         if(hidden){
8814             this.CSS.updateRule(thSelector, "display", "none");
8815             this.CSS.updateRule(tdSelector, "display", "none");
8816         }
8817         
8818         this.onHeaderChange();
8819         this.onLoad();
8820     },
8821     
8822     setColumnWidth: function(col_index, width)
8823     {
8824         // width = "md-2 xs-2..."
8825         if(!this.colModel.config[col_index]) {
8826             return;
8827         }
8828         
8829         var w = width.split(" ");
8830         
8831         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832         
8833         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834         
8835         
8836         for(var j = 0; j < w.length; j++) {
8837             
8838             if(!w[j]) {
8839                 continue;
8840             }
8841             
8842             var size_cls = w[j].split("-");
8843             
8844             if(!Number.isInteger(size_cls[1] * 1)) {
8845                 continue;
8846             }
8847             
8848             if(!this.colModel.config[col_index][size_cls[0]]) {
8849                 continue;
8850             }
8851             
8852             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853                 continue;
8854             }
8855             
8856             h_row[0].classList.replace(
8857                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858                 "col-"+size_cls[0]+"-"+size_cls[1]
8859             );
8860             
8861             for(var i = 0; i < rows.length; i++) {
8862                 
8863                 var size_cls = w[j].split("-");
8864                 
8865                 if(!Number.isInteger(size_cls[1] * 1)) {
8866                     continue;
8867                 }
8868                 
8869                 if(!this.colModel.config[col_index][size_cls[0]]) {
8870                     continue;
8871                 }
8872                 
8873                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874                     continue;
8875                 }
8876                 
8877                 rows[i].classList.replace(
8878                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879                     "col-"+size_cls[0]+"-"+size_cls[1]
8880                 );
8881             }
8882             
8883             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884         }
8885     }
8886 });
8887
8888  
8889
8890  /*
8891  * - LGPL
8892  *
8893  * table cell
8894  * 
8895  */
8896
8897 /**
8898  * @class Roo.bootstrap.TableCell
8899  * @extends Roo.bootstrap.Component
8900  * Bootstrap TableCell class
8901  * @cfg {String} html cell contain text
8902  * @cfg {String} cls cell class
8903  * @cfg {String} tag cell tag (td|th) default td
8904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905  * @cfg {String} align Aligns the content in a cell
8906  * @cfg {String} axis Categorizes cells
8907  * @cfg {String} bgcolor Specifies the background color of a cell
8908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909  * @cfg {Number} colspan Specifies the number of columns a cell should span
8910  * @cfg {String} headers Specifies one or more header cells a cell is related to
8911  * @cfg {Number} height Sets the height of a cell
8912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913  * @cfg {Number} rowspan Sets the number of rows a cell should span
8914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915  * @cfg {String} valign Vertical aligns the content in a cell
8916  * @cfg {Number} width Specifies the width of a cell
8917  * 
8918  * @constructor
8919  * Create a new TableCell
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.TableCell = function(config){
8924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 };
8926
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8928     
8929     html: false,
8930     cls: false,
8931     tag: false,
8932     abbr: false,
8933     align: false,
8934     axis: false,
8935     bgcolor: false,
8936     charoff: false,
8937     colspan: false,
8938     headers: false,
8939     height: false,
8940     nowrap: false,
8941     rowspan: false,
8942     scope: false,
8943     valign: false,
8944     width: false,
8945     
8946     
8947     getAutoCreate : function(){
8948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'td'
8952         };
8953         
8954         if(this.tag){
8955             cfg.tag = this.tag;
8956         }
8957         
8958         if (this.html) {
8959             cfg.html=this.html
8960         }
8961         if (this.cls) {
8962             cfg.cls=this.cls
8963         }
8964         if (this.abbr) {
8965             cfg.abbr=this.abbr
8966         }
8967         if (this.align) {
8968             cfg.align=this.align
8969         }
8970         if (this.axis) {
8971             cfg.axis=this.axis
8972         }
8973         if (this.bgcolor) {
8974             cfg.bgcolor=this.bgcolor
8975         }
8976         if (this.charoff) {
8977             cfg.charoff=this.charoff
8978         }
8979         if (this.colspan) {
8980             cfg.colspan=this.colspan
8981         }
8982         if (this.headers) {
8983             cfg.headers=this.headers
8984         }
8985         if (this.height) {
8986             cfg.height=this.height
8987         }
8988         if (this.nowrap) {
8989             cfg.nowrap=this.nowrap
8990         }
8991         if (this.rowspan) {
8992             cfg.rowspan=this.rowspan
8993         }
8994         if (this.scope) {
8995             cfg.scope=this.scope
8996         }
8997         if (this.valign) {
8998             cfg.valign=this.valign
8999         }
9000         if (this.width) {
9001             cfg.width=this.width
9002         }
9003         
9004         
9005         return cfg;
9006     }
9007    
9008 });
9009
9010  
9011
9012  /*
9013  * - LGPL
9014  *
9015  * table row
9016  * 
9017  */
9018
9019 /**
9020  * @class Roo.bootstrap.TableRow
9021  * @extends Roo.bootstrap.Component
9022  * Bootstrap TableRow class
9023  * @cfg {String} cls row class
9024  * @cfg {String} align Aligns the content in a table row
9025  * @cfg {String} bgcolor Specifies a background color for a table row
9026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027  * @cfg {String} valign Vertical aligns the content in a table row
9028  * 
9029  * @constructor
9030  * Create a new TableRow
9031  * @param {Object} config The config object
9032  */
9033
9034 Roo.bootstrap.TableRow = function(config){
9035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 };
9037
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9039     
9040     cls: false,
9041     align: false,
9042     bgcolor: false,
9043     charoff: false,
9044     valign: false,
9045     
9046     getAutoCreate : function(){
9047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9048         
9049         cfg = {
9050             tag: 'tr'
9051         };
9052             
9053         if(this.cls){
9054             cfg.cls = this.cls;
9055         }
9056         if(this.align){
9057             cfg.align = this.align;
9058         }
9059         if(this.bgcolor){
9060             cfg.bgcolor = this.bgcolor;
9061         }
9062         if(this.charoff){
9063             cfg.charoff = this.charoff;
9064         }
9065         if(this.valign){
9066             cfg.valign = this.valign;
9067         }
9068         
9069         return cfg;
9070     }
9071    
9072 });
9073
9074  
9075
9076  /*
9077  * - LGPL
9078  *
9079  * table body
9080  * 
9081  */
9082
9083 /**
9084  * @class Roo.bootstrap.TableBody
9085  * @extends Roo.bootstrap.Component
9086  * Bootstrap TableBody class
9087  * @cfg {String} cls element class
9088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089  * @cfg {String} align Aligns the content inside the element
9090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092  * 
9093  * @constructor
9094  * Create a new TableBody
9095  * @param {Object} config The config object
9096  */
9097
9098 Roo.bootstrap.TableBody = function(config){
9099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 };
9101
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9103     
9104     cls: false,
9105     tag: false,
9106     align: false,
9107     charoff: false,
9108     valign: false,
9109     
9110     getAutoCreate : function(){
9111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112         
9113         cfg = {
9114             tag: 'tbody'
9115         };
9116             
9117         if (this.cls) {
9118             cfg.cls=this.cls
9119         }
9120         if(this.tag){
9121             cfg.tag = this.tag;
9122         }
9123         
9124         if(this.align){
9125             cfg.align = this.align;
9126         }
9127         if(this.charoff){
9128             cfg.charoff = this.charoff;
9129         }
9130         if(this.valign){
9131             cfg.valign = this.valign;
9132         }
9133         
9134         return cfg;
9135     }
9136     
9137     
9138 //    initEvents : function()
9139 //    {
9140 //        
9141 //        if(!this.store){
9142 //            return;
9143 //        }
9144 //        
9145 //        this.store = Roo.factory(this.store, Roo.data);
9146 //        this.store.on('load', this.onLoad, this);
9147 //        
9148 //        this.store.load();
9149 //        
9150 //    },
9151 //    
9152 //    onLoad: function () 
9153 //    {   
9154 //        this.fireEvent('load', this);
9155 //    }
9156 //    
9157 //   
9158 });
9159
9160  
9161
9162  /*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9175  /**
9176  * @class Roo.form.Action
9177  * Internal Class used to handle form actions
9178  * @constructor
9179  * @param {Roo.form.BasicForm} el The form element or its id
9180  * @param {Object} config Configuration options
9181  */
9182
9183  
9184  
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9187     this.form = form;
9188     this.options = options || {};
9189 };
9190 /**
9191  * Client Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9195 /**
9196  * Server Validation Failed
9197  * @const 
9198  */
9199 Roo.form.Action.SERVER_INVALID = 'server';
9200  /**
9201  * Connect to Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 /**
9206  * Reading Data from Server Failed
9207  * @const 
9208  */
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9210
9211 Roo.form.Action.prototype = {
9212     type : 'default',
9213     failureType : undefined,
9214     response : undefined,
9215     result : undefined,
9216
9217     // interface method
9218     run : function(options){
9219
9220     },
9221
9222     // interface method
9223     success : function(response){
9224
9225     },
9226
9227     // interface method
9228     handleResponse : function(response){
9229
9230     },
9231
9232     // default connection failure
9233     failure : function(response){
9234         
9235         this.response = response;
9236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237         this.form.afterAction(this, false);
9238     },
9239
9240     processResponse : function(response){
9241         this.response = response;
9242         if(!response.responseText){
9243             return true;
9244         }
9245         this.result = this.handleResponse(response);
9246         return this.result;
9247     },
9248
9249     // utility functions used internally
9250     getUrl : function(appendParams){
9251         var url = this.options.url || this.form.url || this.form.el.dom.action;
9252         if(appendParams){
9253             var p = this.getParams();
9254             if(p){
9255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256             }
9257         }
9258         return url;
9259     },
9260
9261     getMethod : function(){
9262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263     },
9264
9265     getParams : function(){
9266         var bp = this.form.baseParams;
9267         var p = this.options.params;
9268         if(p){
9269             if(typeof p == "object"){
9270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271             }else if(typeof p == 'string' && bp){
9272                 p += '&' + Roo.urlEncode(bp);
9273             }
9274         }else if(bp){
9275             p = Roo.urlEncode(bp);
9276         }
9277         return p;
9278     },
9279
9280     createCallback : function(){
9281         return {
9282             success: this.success,
9283             failure: this.failure,
9284             scope: this,
9285             timeout: (this.form.timeout*1000),
9286             upload: this.form.fileUpload ? this.success : undefined
9287         };
9288     }
9289 };
9290
9291 Roo.form.Action.Submit = function(form, options){
9292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 };
9294
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296     type : 'submit',
9297
9298     haveProgress : false,
9299     uploadComplete : false,
9300     
9301     // uploadProgress indicator.
9302     uploadProgress : function()
9303     {
9304         if (!this.form.progressUrl) {
9305             return;
9306         }
9307         
9308         if (!this.haveProgress) {
9309             Roo.MessageBox.progress("Uploading", "Uploading");
9310         }
9311         if (this.uploadComplete) {
9312            Roo.MessageBox.hide();
9313            return;
9314         }
9315         
9316         this.haveProgress = true;
9317    
9318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319         
9320         var c = new Roo.data.Connection();
9321         c.request({
9322             url : this.form.progressUrl,
9323             params: {
9324                 id : uid
9325             },
9326             method: 'GET',
9327             success : function(req){
9328                //console.log(data);
9329                 var rdata = false;
9330                 var edata;
9331                 try  {
9332                    rdata = Roo.decode(req.responseText)
9333                 } catch (e) {
9334                     Roo.log("Invalid data from server..");
9335                     Roo.log(edata);
9336                     return;
9337                 }
9338                 if (!rdata || !rdata.success) {
9339                     Roo.log(rdata);
9340                     Roo.MessageBox.alert(Roo.encode(rdata));
9341                     return;
9342                 }
9343                 var data = rdata.data;
9344                 
9345                 if (this.uploadComplete) {
9346                    Roo.MessageBox.hide();
9347                    return;
9348                 }
9349                    
9350                 if (data){
9351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353                     );
9354                 }
9355                 this.uploadProgress.defer(2000,this);
9356             },
9357        
9358             failure: function(data) {
9359                 Roo.log('progress url failed ');
9360                 Roo.log(data);
9361             },
9362             scope : this
9363         });
9364            
9365     },
9366     
9367     
9368     run : function()
9369     {
9370         // run get Values on the form, so it syncs any secondary forms.
9371         this.form.getValues();
9372         
9373         var o = this.options;
9374         var method = this.getMethod();
9375         var isPost = method == 'POST';
9376         if(o.clientValidation === false || this.form.isValid()){
9377             
9378             if (this.form.progressUrl) {
9379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380                     (new Date() * 1) + '' + Math.random());
9381                     
9382             } 
9383             
9384             
9385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386                 form:this.form.el.dom,
9387                 url:this.getUrl(!isPost),
9388                 method: method,
9389                 params:isPost ? this.getParams() : null,
9390                 isUpload: this.form.fileUpload,
9391                 formData : this.form.formData
9392             }));
9393             
9394             this.uploadProgress();
9395
9396         }else if (o.clientValidation !== false){ // client validation failed
9397             this.failureType = Roo.form.Action.CLIENT_INVALID;
9398             this.form.afterAction(this, false);
9399         }
9400     },
9401
9402     success : function(response)
9403     {
9404         this.uploadComplete= true;
9405         if (this.haveProgress) {
9406             Roo.MessageBox.hide();
9407         }
9408         
9409         
9410         var result = this.processResponse(response);
9411         if(result === true || result.success){
9412             this.form.afterAction(this, true);
9413             return;
9414         }
9415         if(result.errors){
9416             this.form.markInvalid(result.errors);
9417             this.failureType = Roo.form.Action.SERVER_INVALID;
9418         }
9419         this.form.afterAction(this, false);
9420     },
9421     failure : function(response)
9422     {
9423         this.uploadComplete= true;
9424         if (this.haveProgress) {
9425             Roo.MessageBox.hide();
9426         }
9427         
9428         this.response = response;
9429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430         this.form.afterAction(this, false);
9431     },
9432     
9433     handleResponse : function(response){
9434         if(this.form.errorReader){
9435             var rs = this.form.errorReader.read(response);
9436             var errors = [];
9437             if(rs.records){
9438                 for(var i = 0, len = rs.records.length; i < len; i++) {
9439                     var r = rs.records[i];
9440                     errors[i] = r.data;
9441                 }
9442             }
9443             if(errors.length < 1){
9444                 errors = null;
9445             }
9446             return {
9447                 success : rs.success,
9448                 errors : errors
9449             };
9450         }
9451         var ret = false;
9452         try {
9453             ret = Roo.decode(response.responseText);
9454         } catch (e) {
9455             ret = {
9456                 success: false,
9457                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9458                 errors : []
9459             };
9460         }
9461         return ret;
9462         
9463     }
9464 });
9465
9466
9467 Roo.form.Action.Load = function(form, options){
9468     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469     this.reader = this.form.reader;
9470 };
9471
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9473     type : 'load',
9474
9475     run : function(){
9476         
9477         Roo.Ajax.request(Roo.apply(
9478                 this.createCallback(), {
9479                     method:this.getMethod(),
9480                     url:this.getUrl(false),
9481                     params:this.getParams()
9482         }));
9483     },
9484
9485     success : function(response){
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || !result.success || !result.data){
9489             this.failureType = Roo.form.Action.LOAD_FAILURE;
9490             this.form.afterAction(this, false);
9491             return;
9492         }
9493         this.form.clearInvalid();
9494         this.form.setValues(result.data);
9495         this.form.afterAction(this, true);
9496     },
9497
9498     handleResponse : function(response){
9499         if(this.form.reader){
9500             var rs = this.form.reader.read(response);
9501             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502             return {
9503                 success : rs.success,
9504                 data : data
9505             };
9506         }
9507         return Roo.decode(response.responseText);
9508     }
9509 });
9510
9511 Roo.form.Action.ACTION_TYPES = {
9512     'load' : Roo.form.Action.Load,
9513     'submit' : Roo.form.Action.Submit
9514 };/*
9515  * - LGPL
9516  *
9517  * form
9518  *
9519  */
9520
9521 /**
9522  * @class Roo.bootstrap.Form
9523  * @extends Roo.bootstrap.Component
9524  * Bootstrap Form class
9525  * @cfg {String} method  GET | POST (default POST)
9526  * @cfg {String} labelAlign top | left (default top)
9527  * @cfg {String} align left  | right - for navbars
9528  * @cfg {Boolean} loadMask load mask when submit (default true)
9529
9530  *
9531  * @constructor
9532  * Create a new Form
9533  * @param {Object} config The config object
9534  */
9535
9536
9537 Roo.bootstrap.Form = function(config){
9538     
9539     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540     
9541     Roo.bootstrap.Form.popover.apply();
9542     
9543     this.addEvents({
9544         /**
9545          * @event clientvalidation
9546          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547          * @param {Form} this
9548          * @param {Boolean} valid true if the form has passed client-side validation
9549          */
9550         clientvalidation: true,
9551         /**
9552          * @event beforeaction
9553          * Fires before any action is performed. Return false to cancel the action.
9554          * @param {Form} this
9555          * @param {Action} action The action to be performed
9556          */
9557         beforeaction: true,
9558         /**
9559          * @event actionfailed
9560          * Fires when an action fails.
9561          * @param {Form} this
9562          * @param {Action} action The action that failed
9563          */
9564         actionfailed : true,
9565         /**
9566          * @event actioncomplete
9567          * Fires when an action is completed.
9568          * @param {Form} this
9569          * @param {Action} action The action that completed
9570          */
9571         actioncomplete : true
9572     });
9573 };
9574
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9576
9577      /**
9578      * @cfg {String} method
9579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9580      */
9581     method : 'POST',
9582     /**
9583      * @cfg {String} url
9584      * The URL to use for form actions if one isn't supplied in the action options.
9585      */
9586     /**
9587      * @cfg {Boolean} fileUpload
9588      * Set to true if this form is a file upload.
9589      */
9590
9591     /**
9592      * @cfg {Object} baseParams
9593      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594      */
9595
9596     /**
9597      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598      */
9599     timeout: 30,
9600     /**
9601      * @cfg {Sting} align (left|right) for navbar forms
9602      */
9603     align : 'left',
9604
9605     // private
9606     activeAction : null,
9607
9608     /**
9609      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610      * element by passing it or its id or mask the form itself by passing in true.
9611      * @type Mixed
9612      */
9613     waitMsgTarget : false,
9614
9615     loadMask : true,
9616     
9617     /**
9618      * @cfg {Boolean} errorMask (true|false) default false
9619      */
9620     errorMask : false,
9621     
9622     /**
9623      * @cfg {Number} maskOffset Default 100
9624      */
9625     maskOffset : 100,
9626     
9627     /**
9628      * @cfg {Boolean} maskBody
9629      */
9630     maskBody : false,
9631
9632     getAutoCreate : function(){
9633
9634         var cfg = {
9635             tag: 'form',
9636             method : this.method || 'POST',
9637             id : this.id || Roo.id(),
9638             cls : ''
9639         };
9640         if (this.parent().xtype.match(/^Nav/)) {
9641             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642
9643         }
9644
9645         if (this.labelAlign == 'left' ) {
9646             cfg.cls += ' form-horizontal';
9647         }
9648
9649
9650         return cfg;
9651     },
9652     initEvents : function()
9653     {
9654         this.el.on('submit', this.onSubmit, this);
9655         // this was added as random key presses on the form where triggering form submit.
9656         this.el.on('keypress', function(e) {
9657             if (e.getCharCode() != 13) {
9658                 return true;
9659             }
9660             // we might need to allow it for textareas.. and some other items.
9661             // check e.getTarget().
9662
9663             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664                 return true;
9665             }
9666
9667             Roo.log("keypress blocked");
9668
9669             e.preventDefault();
9670             return false;
9671         });
9672         
9673     },
9674     // private
9675     onSubmit : function(e){
9676         e.stopEvent();
9677     },
9678
9679      /**
9680      * Returns true if client-side validation on the form is successful.
9681      * @return Boolean
9682      */
9683     isValid : function(){
9684         var items = this.getItems();
9685         var valid = true;
9686         var target = false;
9687         
9688         items.each(function(f){
9689             
9690             if(f.validate()){
9691                 return;
9692             }
9693             
9694             Roo.log('invalid field: ' + f.name);
9695             
9696             valid = false;
9697
9698             if(!target && f.el.isVisible(true)){
9699                 target = f;
9700             }
9701            
9702         });
9703         
9704         if(this.errorMask && !valid){
9705             Roo.bootstrap.Form.popover.mask(this, target);
9706         }
9707         
9708         return valid;
9709     },
9710     
9711     /**
9712      * Returns true if any fields in this form have changed since their original load.
9713      * @return Boolean
9714      */
9715     isDirty : function(){
9716         var dirty = false;
9717         var items = this.getItems();
9718         items.each(function(f){
9719            if(f.isDirty()){
9720                dirty = true;
9721                return false;
9722            }
9723            return true;
9724         });
9725         return dirty;
9726     },
9727      /**
9728      * Performs a predefined action (submit or load) or custom actions you define on this form.
9729      * @param {String} actionName The name of the action type
9730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732      * accept other config options):
9733      * <pre>
9734 Property          Type             Description
9735 ----------------  ---------------  ----------------------------------------------------------------------------------
9736 url               String           The url for the action (defaults to the form's url)
9737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9740                                    validate the form on the client (defaults to false)
9741      * </pre>
9742      * @return {BasicForm} this
9743      */
9744     doAction : function(action, options){
9745         if(typeof action == 'string'){
9746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747         }
9748         if(this.fireEvent('beforeaction', this, action) !== false){
9749             this.beforeAction(action);
9750             action.run.defer(100, action);
9751         }
9752         return this;
9753     },
9754
9755     // private
9756     beforeAction : function(action){
9757         var o = action.options;
9758         
9759         if(this.loadMask){
9760             
9761             if(this.maskBody){
9762                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763             } else {
9764                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765             }
9766         }
9767         // not really supported yet.. ??
9768
9769         //if(this.waitMsgTarget === true){
9770         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else if(this.waitMsgTarget){
9772         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else {
9775         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776        // }
9777
9778     },
9779
9780     // private
9781     afterAction : function(action, success){
9782         this.activeAction = null;
9783         var o = action.options;
9784
9785         if(this.loadMask){
9786             
9787             if(this.maskBody){
9788                 Roo.get(document.body).unmask();
9789             } else {
9790                 this.el.unmask();
9791             }
9792         }
9793         
9794         //if(this.waitMsgTarget === true){
9795 //            this.el.unmask();
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget.unmask();
9798         //}else{
9799         //    Roo.MessageBox.updateProgress(1);
9800         //    Roo.MessageBox.hide();
9801        // }
9802         //
9803         if(success){
9804             if(o.reset){
9805                 this.reset();
9806             }
9807             Roo.callback(o.success, o.scope, [this, action]);
9808             this.fireEvent('actioncomplete', this, action);
9809
9810         }else{
9811
9812             // failure condition..
9813             // we have a scenario where updates need confirming.
9814             // eg. if a locking scenario exists..
9815             // we look for { errors : { needs_confirm : true }} in the response.
9816             if (
9817                 (typeof(action.result) != 'undefined')  &&
9818                 (typeof(action.result.errors) != 'undefined')  &&
9819                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820            ){
9821                 var _t = this;
9822                 Roo.log("not supported yet");
9823                  /*
9824
9825                 Roo.MessageBox.confirm(
9826                     "Change requires confirmation",
9827                     action.result.errorMsg,
9828                     function(r) {
9829                         if (r != 'yes') {
9830                             return;
9831                         }
9832                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9833                     }
9834
9835                 );
9836                 */
9837
9838
9839                 return;
9840             }
9841
9842             Roo.callback(o.failure, o.scope, [this, action]);
9843             // show an error message if no failed handler is set..
9844             if (!this.hasListener('actionfailed')) {
9845                 Roo.log("need to add dialog support");
9846                 /*
9847                 Roo.MessageBox.alert("Error",
9848                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849                         action.result.errorMsg :
9850                         "Saving Failed, please check your entries or try again"
9851                 );
9852                 */
9853             }
9854
9855             this.fireEvent('actionfailed', this, action);
9856         }
9857
9858     },
9859     /**
9860      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861      * @param {String} id The value to search for
9862      * @return Field
9863      */
9864     findField : function(id){
9865         var items = this.getItems();
9866         var field = items.get(id);
9867         if(!field){
9868              items.each(function(f){
9869                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9870                     field = f;
9871                     return false;
9872                 }
9873                 return true;
9874             });
9875         }
9876         return field || null;
9877     },
9878      /**
9879      * Mark fields in this form invalid in bulk.
9880      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881      * @return {BasicForm} this
9882      */
9883     markInvalid : function(errors){
9884         if(errors instanceof Array){
9885             for(var i = 0, len = errors.length; i < len; i++){
9886                 var fieldError = errors[i];
9887                 var f = this.findField(fieldError.id);
9888                 if(f){
9889                     f.markInvalid(fieldError.msg);
9890                 }
9891             }
9892         }else{
9893             var field, id;
9894             for(id in errors){
9895                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896                     field.markInvalid(errors[id]);
9897                 }
9898             }
9899         }
9900         //Roo.each(this.childForms || [], function (f) {
9901         //    f.markInvalid(errors);
9902         //});
9903
9904         return this;
9905     },
9906
9907     /**
9908      * Set values for fields in this form in bulk.
9909      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910      * @return {BasicForm} this
9911      */
9912     setValues : function(values){
9913         if(values instanceof Array){ // array of objects
9914             for(var i = 0, len = values.length; i < len; i++){
9915                 var v = values[i];
9916                 var f = this.findField(v.id);
9917                 if(f){
9918                     f.setValue(v.value);
9919                     if(this.trackResetOnLoad){
9920                         f.originalValue = f.getValue();
9921                     }
9922                 }
9923             }
9924         }else{ // object hash
9925             var field, id;
9926             for(id in values){
9927                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928
9929                     if (field.setFromData &&
9930                         field.valueField &&
9931                         field.displayField &&
9932                         // combos' with local stores can
9933                         // be queried via setValue()
9934                         // to set their value..
9935                         (field.store && !field.store.isLocal)
9936                         ) {
9937                         // it's a combo
9938                         var sd = { };
9939                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941                         field.setFromData(sd);
9942
9943                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944                         
9945                         field.setFromData(values);
9946                         
9947                     } else {
9948                         field.setValue(values[id]);
9949                     }
9950
9951
9952                     if(this.trackResetOnLoad){
9953                         field.originalValue = field.getValue();
9954                     }
9955                 }
9956             }
9957         }
9958
9959         //Roo.each(this.childForms || [], function (f) {
9960         //    f.setValues(values);
9961         //});
9962
9963         return this;
9964     },
9965
9966     /**
9967      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968      * they are returned as an array.
9969      * @param {Boolean} asString
9970      * @return {Object}
9971      */
9972     getValues : function(asString){
9973         //if (this.childForms) {
9974             // copy values from the child forms
9975         //    Roo.each(this.childForms, function (f) {
9976         //        this.setValues(f.getValues());
9977         //    }, this);
9978         //}
9979
9980
9981
9982         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983         if(asString === true){
9984             return fs;
9985         }
9986         return Roo.urlDecode(fs);
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs.
9991      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992      * @return {Object}
9993      */
9994     getFieldValues : function(with_hidden)
9995     {
9996         var items = this.getItems();
9997         var ret = {};
9998         items.each(function(f){
9999             
10000             if (!f.getName()) {
10001                 return;
10002             }
10003             
10004             var v = f.getValue();
10005             
10006             if (f.inputType =='radio') {
10007                 if (typeof(ret[f.getName()]) == 'undefined') {
10008                     ret[f.getName()] = ''; // empty..
10009                 }
10010
10011                 if (!f.el.dom.checked) {
10012                     return;
10013
10014                 }
10015                 v = f.el.dom.value;
10016
10017             }
10018             
10019             if(f.xtype == 'MoneyField'){
10020                 ret[f.currencyName] = f.getCurrency();
10021             }
10022
10023             // not sure if this supported any more..
10024             if ((typeof(v) == 'object') && f.getRawValue) {
10025                 v = f.getRawValue() ; // dates..
10026             }
10027             // combo boxes where name != hiddenName...
10028             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029                 ret[f.name] = f.getRawValue();
10030             }
10031             ret[f.getName()] = v;
10032         });
10033
10034         return ret;
10035     },
10036
10037     /**
10038      * Clears all invalid messages in this form.
10039      * @return {BasicForm} this
10040      */
10041     clearInvalid : function(){
10042         var items = this.getItems();
10043
10044         items.each(function(f){
10045            f.clearInvalid();
10046         });
10047
10048         return this;
10049     },
10050
10051     /**
10052      * Resets this form.
10053      * @return {BasicForm} this
10054      */
10055     reset : function(){
10056         var items = this.getItems();
10057         items.each(function(f){
10058             f.reset();
10059         });
10060
10061         Roo.each(this.childForms || [], function (f) {
10062             f.reset();
10063         });
10064
10065
10066         return this;
10067     },
10068     
10069     getItems : function()
10070     {
10071         var r=new Roo.util.MixedCollection(false, function(o){
10072             return o.id || (o.id = Roo.id());
10073         });
10074         var iter = function(el) {
10075             if (el.inputEl) {
10076                 r.add(el);
10077             }
10078             if (!el.items) {
10079                 return;
10080             }
10081             Roo.each(el.items,function(e) {
10082                 iter(e);
10083             });
10084         };
10085
10086         iter(this);
10087         return r;
10088     },
10089     
10090     hideFields : function(items)
10091     {
10092         Roo.each(items, function(i){
10093             
10094             var f = this.findField(i);
10095             
10096             if(!f){
10097                 return;
10098             }
10099             
10100             f.hide();
10101             
10102         }, this);
10103     },
10104     
10105     showFields : function(items)
10106     {
10107         Roo.each(items, function(i){
10108             
10109             var f = this.findField(i);
10110             
10111             if(!f){
10112                 return;
10113             }
10114             
10115             f.show();
10116             
10117         }, this);
10118     }
10119
10120 });
10121
10122 Roo.apply(Roo.bootstrap.Form, {
10123     
10124     popover : {
10125         
10126         padding : 5,
10127         
10128         isApplied : false,
10129         
10130         isMasked : false,
10131         
10132         form : false,
10133         
10134         target : false,
10135         
10136         toolTip : false,
10137         
10138         intervalID : false,
10139         
10140         maskEl : false,
10141         
10142         apply : function()
10143         {
10144             if(this.isApplied){
10145                 return;
10146             }
10147             
10148             this.maskEl = {
10149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153             };
10154             
10155             this.maskEl.top.enableDisplayMode("block");
10156             this.maskEl.left.enableDisplayMode("block");
10157             this.maskEl.bottom.enableDisplayMode("block");
10158             this.maskEl.right.enableDisplayMode("block");
10159             
10160             this.toolTip = new Roo.bootstrap.Tooltip({
10161                 cls : 'roo-form-error-popover',
10162                 alignment : {
10163                     'left' : ['r-l', [-2,0], 'right'],
10164                     'right' : ['l-r', [2,0], 'left'],
10165                     'bottom' : ['tl-bl', [0,2], 'top'],
10166                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10167                 }
10168             });
10169             
10170             this.toolTip.render(Roo.get(document.body));
10171
10172             this.toolTip.el.enableDisplayMode("block");
10173             
10174             Roo.get(document.body).on('click', function(){
10175                 this.unmask();
10176             }, this);
10177             
10178             Roo.get(document.body).on('touchstart', function(){
10179                 this.unmask();
10180             }, this);
10181             
10182             this.isApplied = true
10183         },
10184         
10185         mask : function(form, target)
10186         {
10187             this.form = form;
10188             
10189             this.target = target;
10190             
10191             if(!this.form.errorMask || !target.el){
10192                 return;
10193             }
10194             
10195             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196             
10197             Roo.log(scrollable);
10198             
10199             var ot = this.target.el.calcOffsetsTo(scrollable);
10200             
10201             var scrollTo = ot[1] - this.form.maskOffset;
10202             
10203             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204             
10205             scrollable.scrollTo('top', scrollTo);
10206             
10207             var box = this.target.el.getBox();
10208             Roo.log(box);
10209             var zIndex = Roo.bootstrap.Modal.zIndex++;
10210
10211             
10212             this.maskEl.top.setStyle('position', 'absolute');
10213             this.maskEl.top.setStyle('z-index', zIndex);
10214             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215             this.maskEl.top.setLeft(0);
10216             this.maskEl.top.setTop(0);
10217             this.maskEl.top.show();
10218             
10219             this.maskEl.left.setStyle('position', 'absolute');
10220             this.maskEl.left.setStyle('z-index', zIndex);
10221             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222             this.maskEl.left.setLeft(0);
10223             this.maskEl.left.setTop(box.y - this.padding);
10224             this.maskEl.left.show();
10225
10226             this.maskEl.bottom.setStyle('position', 'absolute');
10227             this.maskEl.bottom.setStyle('z-index', zIndex);
10228             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229             this.maskEl.bottom.setLeft(0);
10230             this.maskEl.bottom.setTop(box.bottom + this.padding);
10231             this.maskEl.bottom.show();
10232
10233             this.maskEl.right.setStyle('position', 'absolute');
10234             this.maskEl.right.setStyle('z-index', zIndex);
10235             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236             this.maskEl.right.setLeft(box.right + this.padding);
10237             this.maskEl.right.setTop(box.y - this.padding);
10238             this.maskEl.right.show();
10239
10240             this.toolTip.bindEl = this.target.el;
10241
10242             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243
10244             var tip = this.target.blankText;
10245
10246             if(this.target.getValue() !== '' ) {
10247                 
10248                 if (this.target.invalidText.length) {
10249                     tip = this.target.invalidText;
10250                 } else if (this.target.regexText.length){
10251                     tip = this.target.regexText;
10252                 }
10253             }
10254
10255             this.toolTip.show(tip);
10256
10257             this.intervalID = window.setInterval(function() {
10258                 Roo.bootstrap.Form.popover.unmask();
10259             }, 10000);
10260
10261             window.onwheel = function(){ return false;};
10262             
10263             (function(){ this.isMasked = true; }).defer(500, this);
10264             
10265         },
10266         
10267         unmask : function()
10268         {
10269             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270                 return;
10271             }
10272             
10273             this.maskEl.top.setStyle('position', 'absolute');
10274             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.top.hide();
10276
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279             this.maskEl.left.hide();
10280
10281             this.maskEl.bottom.setStyle('position', 'absolute');
10282             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283             this.maskEl.bottom.hide();
10284
10285             this.maskEl.right.setStyle('position', 'absolute');
10286             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287             this.maskEl.right.hide();
10288             
10289             this.toolTip.hide();
10290             
10291             this.toolTip.el.hide();
10292             
10293             window.onwheel = function(){ return true;};
10294             
10295             if(this.intervalID){
10296                 window.clearInterval(this.intervalID);
10297                 this.intervalID = false;
10298             }
10299             
10300             this.isMasked = false;
10301             
10302         }
10303         
10304     }
10305     
10306 });
10307
10308 /*
10309  * Based on:
10310  * Ext JS Library 1.1.1
10311  * Copyright(c) 2006-2007, Ext JS, LLC.
10312  *
10313  * Originally Released Under LGPL - original licence link has changed is not relivant.
10314  *
10315  * Fork - LGPL
10316  * <script type="text/javascript">
10317  */
10318 /**
10319  * @class Roo.form.VTypes
10320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321  * @singleton
10322  */
10323 Roo.form.VTypes = function(){
10324     // closure these in so they are only created once.
10325     var alpha = /^[a-zA-Z_]+$/;
10326     var alphanum = /^[a-zA-Z0-9_]+$/;
10327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329
10330     // All these messages and functions are configurable
10331     return {
10332         /**
10333          * The function used to validate email addresses
10334          * @param {String} value The email address
10335          */
10336         'email' : function(v){
10337             return email.test(v);
10338         },
10339         /**
10340          * The error text to display when the email validation function returns false
10341          * @type String
10342          */
10343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344         /**
10345          * The keystroke filter mask to be applied on email input
10346          * @type RegExp
10347          */
10348         'emailMask' : /[a-z0-9_\.\-@]/i,
10349
10350         /**
10351          * The function used to validate URLs
10352          * @param {String} value The URL
10353          */
10354         'url' : function(v){
10355             return url.test(v);
10356         },
10357         /**
10358          * The error text to display when the url validation function returns false
10359          * @type String
10360          */
10361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362         
10363         /**
10364          * The function used to validate alpha values
10365          * @param {String} value The value
10366          */
10367         'alpha' : function(v){
10368             return alpha.test(v);
10369         },
10370         /**
10371          * The error text to display when the alpha validation function returns false
10372          * @type String
10373          */
10374         'alphaText' : 'This field should only contain letters and _',
10375         /**
10376          * The keystroke filter mask to be applied on alpha input
10377          * @type RegExp
10378          */
10379         'alphaMask' : /[a-z_]/i,
10380
10381         /**
10382          * The function used to validate alphanumeric values
10383          * @param {String} value The value
10384          */
10385         'alphanum' : function(v){
10386             return alphanum.test(v);
10387         },
10388         /**
10389          * The error text to display when the alphanumeric validation function returns false
10390          * @type String
10391          */
10392         'alphanumText' : 'This field should only contain letters, numbers and _',
10393         /**
10394          * The keystroke filter mask to be applied on alphanumeric input
10395          * @type RegExp
10396          */
10397         'alphanumMask' : /[a-z0-9_]/i
10398     };
10399 }();/*
10400  * - LGPL
10401  *
10402  * Input
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.Input
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap Input class
10410  * @cfg {Boolean} disabled is it disabled
10411  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10412  * @cfg {String} name name of the input
10413  * @cfg {string} fieldLabel - the label associated
10414  * @cfg {string} placeholder - placeholder to put in text.
10415  * @cfg {string}  before - input group add on before
10416  * @cfg {string} after - input group add on after
10417  * @cfg {string} size - (lg|sm) or leave empty..
10418  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420  * @cfg {Number} md colspan out of 12 for computer-sized screens
10421  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422  * @cfg {string} value default value of the input
10423  * @cfg {Number} labelWidth set the width of label 
10424  * @cfg {Number} labellg set the width of label (1-12)
10425  * @cfg {Number} labelmd set the width of label (1-12)
10426  * @cfg {Number} labelsm set the width of label (1-12)
10427  * @cfg {Number} labelxs set the width of label (1-12)
10428  * @cfg {String} labelAlign (top|left)
10429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431  * @cfg {String} indicatorpos (left|right) default left
10432  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435
10436  * @cfg {String} align (left|center|right) Default left
10437  * @cfg {Boolean} forceFeedback (true|false) Default false
10438  * 
10439  * @constructor
10440  * Create a new Input
10441  * @param {Object} config The config object
10442  */
10443
10444 Roo.bootstrap.Input = function(config){
10445     
10446     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10447     
10448     this.addEvents({
10449         /**
10450          * @event focus
10451          * Fires when this field receives input focus.
10452          * @param {Roo.form.Field} this
10453          */
10454         focus : true,
10455         /**
10456          * @event blur
10457          * Fires when this field loses input focus.
10458          * @param {Roo.form.Field} this
10459          */
10460         blur : true,
10461         /**
10462          * @event specialkey
10463          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10464          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465          * @param {Roo.form.Field} this
10466          * @param {Roo.EventObject} e The event object
10467          */
10468         specialkey : true,
10469         /**
10470          * @event change
10471          * Fires just before the field blurs if the field value has changed.
10472          * @param {Roo.form.Field} this
10473          * @param {Mixed} newValue The new value
10474          * @param {Mixed} oldValue The original value
10475          */
10476         change : true,
10477         /**
10478          * @event invalid
10479          * Fires after the field has been marked as invalid.
10480          * @param {Roo.form.Field} this
10481          * @param {String} msg The validation message
10482          */
10483         invalid : true,
10484         /**
10485          * @event valid
10486          * Fires after the field has been validated with no errors.
10487          * @param {Roo.form.Field} this
10488          */
10489         valid : true,
10490          /**
10491          * @event keyup
10492          * Fires after the key up
10493          * @param {Roo.form.Field} this
10494          * @param {Roo.EventObject}  e The event Object
10495          */
10496         keyup : true
10497     });
10498 };
10499
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10501      /**
10502      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503       automatic validation (defaults to "keyup").
10504      */
10505     validationEvent : "keyup",
10506      /**
10507      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508      */
10509     validateOnBlur : true,
10510     /**
10511      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512      */
10513     validationDelay : 250,
10514      /**
10515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516      */
10517     focusClass : "x-form-focus",  // not needed???
10518     
10519        
10520     /**
10521      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     invalidClass : "has-warning",
10524     
10525     /**
10526      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527      */
10528     validClass : "has-success",
10529     
10530     /**
10531      * @cfg {Boolean} hasFeedback (true|false) default true
10532      */
10533     hasFeedback : true,
10534     
10535     /**
10536      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     invalidFeedbackClass : "glyphicon-warning-sign",
10539     
10540     /**
10541      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542      */
10543     validFeedbackClass : "glyphicon-ok",
10544     
10545     /**
10546      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547      */
10548     selectOnFocus : false,
10549     
10550      /**
10551      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552      */
10553     maskRe : null,
10554        /**
10555      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10556      */
10557     vtype : null,
10558     
10559       /**
10560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561      */
10562     disableKeyFilter : false,
10563     
10564        /**
10565      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566      */
10567     disabled : false,
10568      /**
10569      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570      */
10571     allowBlank : true,
10572     /**
10573      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574      */
10575     blankText : "Please complete this mandatory field",
10576     
10577      /**
10578      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579      */
10580     minLength : 0,
10581     /**
10582      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583      */
10584     maxLength : Number.MAX_VALUE,
10585     /**
10586      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587      */
10588     minLengthText : "The minimum length for this field is {0}",
10589     /**
10590      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591      */
10592     maxLengthText : "The maximum length for this field is {0}",
10593   
10594     
10595     /**
10596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597      * If available, this function will be called only after the basic validators all return true, and will be passed the
10598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599      */
10600     validator : null,
10601     /**
10602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10605      */
10606     regex : null,
10607     /**
10608      * @cfg {String} regexText -- Depricated - use Invalid Text
10609      */
10610     regexText : "",
10611     
10612     /**
10613      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614      */
10615     invalidText : "",
10616     
10617     
10618     
10619     autocomplete: false,
10620     
10621     
10622     fieldLabel : '',
10623     inputType : 'text',
10624     
10625     name : false,
10626     placeholder: false,
10627     before : false,
10628     after : false,
10629     size : false,
10630     hasFocus : false,
10631     preventMark: false,
10632     isFormField : true,
10633     value : '',
10634     labelWidth : 2,
10635     labelAlign : false,
10636     readOnly : false,
10637     align : false,
10638     formatedValue : false,
10639     forceFeedback : false,
10640     
10641     indicatorpos : 'left',
10642     
10643     labellg : 0,
10644     labelmd : 0,
10645     labelsm : 0,
10646     labelxs : 0,
10647     
10648     capture : '',
10649     accept : '',
10650     
10651     parentLabelAlign : function()
10652     {
10653         var parent = this;
10654         while (parent.parent()) {
10655             parent = parent.parent();
10656             if (typeof(parent.labelAlign) !='undefined') {
10657                 return parent.labelAlign;
10658             }
10659         }
10660         return 'left';
10661         
10662     },
10663     
10664     getAutoCreate : function()
10665     {
10666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667         
10668         var id = Roo.id();
10669         
10670         var cfg = {};
10671         
10672         if(this.inputType != 'hidden'){
10673             cfg.cls = 'form-group' //input-group
10674         }
10675         
10676         var input =  {
10677             tag: 'input',
10678             id : id,
10679             type : this.inputType,
10680             value : this.value,
10681             cls : 'form-control',
10682             placeholder : this.placeholder || '',
10683             autocomplete : this.autocomplete || 'new-password'
10684         };
10685         if (this.inputType == 'file') {
10686             input.style = 'overflow:hidden'; // why not in CSS?
10687         }
10688         
10689         if(this.capture.length){
10690             input.capture = this.capture;
10691         }
10692         
10693         if(this.accept.length){
10694             input.accept = this.accept + "/*";
10695         }
10696         
10697         if(this.align){
10698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699         }
10700         
10701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702             input.maxLength = this.maxLength;
10703         }
10704         
10705         if (this.disabled) {
10706             input.disabled=true;
10707         }
10708         
10709         if (this.readOnly) {
10710             input.readonly=true;
10711         }
10712         
10713         if (this.name) {
10714             input.name = this.name;
10715         }
10716         
10717         if (this.size) {
10718             input.cls += ' input-' + this.size;
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         var inputblock = input;
10729         
10730         var feedback = {
10731             tag: 'span',
10732             cls: 'glyphicon form-control-feedback'
10733         };
10734             
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             inputblock = {
10738                 cls : 'has-feedback',
10739                 cn :  [
10740                     input,
10741                     feedback
10742                 ] 
10743             };  
10744         }
10745         
10746         if (this.before || this.after) {
10747             
10748             inputblock = {
10749                 cls : 'input-group',
10750                 cn :  [] 
10751             };
10752             
10753             if (this.before && typeof(this.before) == 'string') {
10754                 
10755                 inputblock.cn.push({
10756                     tag :'span',
10757                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758                     html : this.before
10759                 });
10760             }
10761             if (this.before && typeof(this.before) == 'object') {
10762                 this.before = Roo.factory(this.before);
10763                 
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-before input-group-prepend   input-group-' +
10767                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10768                 });
10769             }
10770             
10771             inputblock.cn.push(input);
10772             
10773             if (this.after && typeof(this.after) == 'string') {
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777                     html : this.after
10778                 });
10779             }
10780             if (this.after && typeof(this.after) == 'object') {
10781                 this.after = Roo.factory(this.after);
10782                 
10783                 inputblock.cn.push({
10784                     tag :'span',
10785                     cls : 'roo-input-after input-group-append  input-group-' +
10786                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10787                 });
10788             }
10789             
10790             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791                 inputblock.cls += ' has-feedback';
10792                 inputblock.cn.push(feedback);
10793             }
10794         };
10795         var indicator = {
10796             tag : 'i',
10797             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798             tooltip : 'This field is required'
10799         };
10800         if (this.allowBlank ) {
10801             indicator.style = this.allowBlank ? ' display:none' : '';
10802         }
10803         if (align ==='left' && this.fieldLabel.length) {
10804             
10805             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10806             
10807             cfg.cn = [
10808                 indicator,
10809                 {
10810                     tag: 'label',
10811                     'for' :  id,
10812                     cls : 'control-label col-form-label',
10813                     html : this.fieldLabel
10814
10815                 },
10816                 {
10817                     cls : "", 
10818                     cn: [
10819                         inputblock
10820                     ]
10821                 }
10822             ];
10823             
10824             var labelCfg = cfg.cn[1];
10825             var contentCfg = cfg.cn[2];
10826             
10827             if(this.indicatorpos == 'right'){
10828                 cfg.cn = [
10829                     {
10830                         tag: 'label',
10831                         'for' :  id,
10832                         cls : 'control-label col-form-label',
10833                         cn : [
10834                             {
10835                                 tag : 'span',
10836                                 html : this.fieldLabel
10837                             },
10838                             indicator
10839                         ]
10840                     },
10841                     {
10842                         cls : "",
10843                         cn: [
10844                             inputblock
10845                         ]
10846                     }
10847
10848                 ];
10849                 
10850                 labelCfg = cfg.cn[0];
10851                 contentCfg = cfg.cn[1];
10852             
10853             }
10854             
10855             if(this.labelWidth > 12){
10856                 labelCfg.style = "width: " + this.labelWidth + 'px';
10857             }
10858             
10859             if(this.labelWidth < 13 && this.labelmd == 0){
10860                 this.labelmd = this.labelWidth;
10861             }
10862             
10863             if(this.labellg > 0){
10864                 labelCfg.cls += ' col-lg-' + this.labellg;
10865                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866             }
10867             
10868             if(this.labelmd > 0){
10869                 labelCfg.cls += ' col-md-' + this.labelmd;
10870                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871             }
10872             
10873             if(this.labelsm > 0){
10874                 labelCfg.cls += ' col-sm-' + this.labelsm;
10875                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876             }
10877             
10878             if(this.labelxs > 0){
10879                 labelCfg.cls += ' col-xs-' + this.labelxs;
10880                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881             }
10882             
10883             
10884         } else if ( this.fieldLabel.length) {
10885                 
10886             
10887             
10888             cfg.cn = [
10889                 {
10890                     tag : 'i',
10891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892                     tooltip : 'This field is required',
10893                     style : this.allowBlank ? ' display:none' : '' 
10894                 },
10895                 {
10896                     tag: 'label',
10897                    //cls : 'input-group-addon',
10898                     html : this.fieldLabel
10899
10900                 },
10901
10902                inputblock
10903
10904            ];
10905            
10906            if(this.indicatorpos == 'right'){
10907        
10908                 cfg.cn = [
10909                     {
10910                         tag: 'label',
10911                        //cls : 'input-group-addon',
10912                         html : this.fieldLabel
10913
10914                     },
10915                     {
10916                         tag : 'i',
10917                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918                         tooltip : 'This field is required',
10919                         style : this.allowBlank ? ' display:none' : '' 
10920                     },
10921
10922                    inputblock
10923
10924                ];
10925
10926             }
10927
10928         } else {
10929             
10930             cfg.cn = [
10931
10932                     inputblock
10933
10934             ];
10935                 
10936                 
10937         };
10938         
10939         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10940            cfg.cls += ' navbar-form';
10941         }
10942         
10943         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944             // on BS4 we do this only if not form 
10945             cfg.cls += ' navbar-form';
10946             cfg.tag = 'li';
10947         }
10948         
10949         return cfg;
10950         
10951     },
10952     /**
10953      * return the real input element.
10954      */
10955     inputEl: function ()
10956     {
10957         return this.el.select('input.form-control',true).first();
10958     },
10959     
10960     tooltipEl : function()
10961     {
10962         return this.inputEl();
10963     },
10964     
10965     indicatorEl : function()
10966     {
10967         if (Roo.bootstrap.version == 4) {
10968             return false; // not enabled in v4 yet.
10969         }
10970         
10971         var indicator = this.el.select('i.roo-required-indicator',true).first();
10972         
10973         if(!indicator){
10974             return false;
10975         }
10976         
10977         return indicator;
10978         
10979     },
10980     
10981     setDisabled : function(v)
10982     {
10983         var i  = this.inputEl().dom;
10984         if (!v) {
10985             i.removeAttribute('disabled');
10986             return;
10987             
10988         }
10989         i.setAttribute('disabled','true');
10990     },
10991     initEvents : function()
10992     {
10993           
10994         this.inputEl().on("keydown" , this.fireKey,  this);
10995         this.inputEl().on("focus", this.onFocus,  this);
10996         this.inputEl().on("blur", this.onBlur,  this);
10997         
10998         this.inputEl().relayEvent('keyup', this);
10999         
11000         this.indicator = this.indicatorEl();
11001         
11002         if(this.indicator){
11003             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11004         }
11005  
11006         // reference to original value for reset
11007         this.originalValue = this.getValue();
11008         //Roo.form.TextField.superclass.initEvents.call(this);
11009         if(this.validationEvent == 'keyup'){
11010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011             this.inputEl().on('keyup', this.filterValidation, this);
11012         }
11013         else if(this.validationEvent !== false){
11014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015         }
11016         
11017         if(this.selectOnFocus){
11018             this.on("focus", this.preFocus, this);
11019             
11020         }
11021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022             this.inputEl().on("keypress", this.filterKeys, this);
11023         } else {
11024             this.inputEl().relayEvent('keypress', this);
11025         }
11026        /* if(this.grow){
11027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11028             this.el.on("click", this.autoSize,  this);
11029         }
11030         */
11031         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033         }
11034         
11035         if (typeof(this.before) == 'object') {
11036             this.before.render(this.el.select('.roo-input-before',true).first());
11037         }
11038         if (typeof(this.after) == 'object') {
11039             this.after.render(this.el.select('.roo-input-after',true).first());
11040         }
11041         
11042         this.inputEl().on('change', this.onChange, this);
11043         
11044     },
11045     filterValidation : function(e){
11046         if(!e.isNavKeyPress()){
11047             this.validationTask.delay(this.validationDelay);
11048         }
11049     },
11050      /**
11051      * Validates the field value
11052      * @return {Boolean} True if the value is valid, else false
11053      */
11054     validate : function(){
11055         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056         if(this.disabled || this.validateValue(this.getRawValue())){
11057             this.markValid();
11058             return true;
11059         }
11060         
11061         this.markInvalid();
11062         return false;
11063     },
11064     
11065     
11066     /**
11067      * Validates a value according to the field's validation rules and marks the field as invalid
11068      * if the validation fails
11069      * @param {Mixed} value The value to validate
11070      * @return {Boolean} True if the value is valid, else false
11071      */
11072     validateValue : function(value)
11073     {
11074         if(this.getVisibilityEl().hasClass('hidden')){
11075             return true;
11076         }
11077         
11078         if(value.length < 1)  { // if it's blank
11079             if(this.allowBlank){
11080                 return true;
11081             }
11082             return false;
11083         }
11084         
11085         if(value.length < this.minLength){
11086             return false;
11087         }
11088         if(value.length > this.maxLength){
11089             return false;
11090         }
11091         if(this.vtype){
11092             var vt = Roo.form.VTypes;
11093             if(!vt[this.vtype](value, this)){
11094                 return false;
11095             }
11096         }
11097         if(typeof this.validator == "function"){
11098             var msg = this.validator(value);
11099             if(msg !== true){
11100                 return false;
11101             }
11102             if (typeof(msg) == 'string') {
11103                 this.invalidText = msg;
11104             }
11105         }
11106         
11107         if(this.regex && !this.regex.test(value)){
11108             return false;
11109         }
11110         
11111         return true;
11112     },
11113     
11114      // private
11115     fireKey : function(e){
11116         //Roo.log('field ' + e.getKey());
11117         if(e.isNavKeyPress()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121     focus : function (selectText){
11122         if(this.rendered){
11123             this.inputEl().focus();
11124             if(selectText === true){
11125                 this.inputEl().dom.select();
11126             }
11127         }
11128         return this;
11129     } ,
11130     
11131     onFocus : function(){
11132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133            // this.el.addClass(this.focusClass);
11134         }
11135         if(!this.hasFocus){
11136             this.hasFocus = true;
11137             this.startValue = this.getValue();
11138             this.fireEvent("focus", this);
11139         }
11140     },
11141     
11142     beforeBlur : Roo.emptyFn,
11143
11144     
11145     // private
11146     onBlur : function(){
11147         this.beforeBlur();
11148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149             //this.el.removeClass(this.focusClass);
11150         }
11151         this.hasFocus = false;
11152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153             this.validate();
11154         }
11155         var v = this.getValue();
11156         if(String(v) !== String(this.startValue)){
11157             this.fireEvent('change', this, v, this.startValue);
11158         }
11159         this.fireEvent("blur", this);
11160     },
11161     
11162     onChange : function(e)
11163     {
11164         var v = this.getValue();
11165         if(String(v) !== String(this.startValue)){
11166             this.fireEvent('change', this, v, this.startValue);
11167         }
11168         
11169     },
11170     
11171     /**
11172      * Resets the current field value to the originally loaded value and clears any validation messages
11173      */
11174     reset : function(){
11175         this.setValue(this.originalValue);
11176         this.validate();
11177     },
11178      /**
11179      * Returns the name of the field
11180      * @return {Mixed} name The name field
11181      */
11182     getName: function(){
11183         return this.name;
11184     },
11185      /**
11186      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getValue : function(){
11190         
11191         var v = this.inputEl().getValue();
11192         
11193         return v;
11194     },
11195     /**
11196      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11197      * @return {Mixed} value The field value
11198      */
11199     getRawValue : function(){
11200         var v = this.inputEl().getValue();
11201         
11202         return v;
11203     },
11204     
11205     /**
11206      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11207      * @param {Mixed} value The value to set
11208      */
11209     setRawValue : function(v){
11210         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211     },
11212     
11213     selectText : function(start, end){
11214         var v = this.getRawValue();
11215         if(v.length > 0){
11216             start = start === undefined ? 0 : start;
11217             end = end === undefined ? v.length : end;
11218             var d = this.inputEl().dom;
11219             if(d.setSelectionRange){
11220                 d.setSelectionRange(start, end);
11221             }else if(d.createTextRange){
11222                 var range = d.createTextRange();
11223                 range.moveStart("character", start);
11224                 range.moveEnd("character", v.length-end);
11225                 range.select();
11226             }
11227         }
11228     },
11229     
11230     /**
11231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11232      * @param {Mixed} value The value to set
11233      */
11234     setValue : function(v){
11235         this.value = v;
11236         if(this.rendered){
11237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238             this.validate();
11239         }
11240     },
11241     
11242     /*
11243     processValue : function(value){
11244         if(this.stripCharsRe){
11245             var newValue = value.replace(this.stripCharsRe, '');
11246             if(newValue !== value){
11247                 this.setRawValue(newValue);
11248                 return newValue;
11249             }
11250         }
11251         return value;
11252     },
11253   */
11254     preFocus : function(){
11255         
11256         if(this.selectOnFocus){
11257             this.inputEl().dom.select();
11258         }
11259     },
11260     filterKeys : function(e){
11261         var k = e.getKey();
11262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263             return;
11264         }
11265         var c = e.getCharCode(), cc = String.fromCharCode(c);
11266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267             return;
11268         }
11269         if(!this.maskRe.test(cc)){
11270             e.stopEvent();
11271         }
11272     },
11273      /**
11274      * Clear any invalid styles/messages for this field
11275      */
11276     clearInvalid : function(){
11277         
11278         if(!this.el || this.preventMark){ // not rendered
11279             return;
11280         }
11281         
11282         
11283         this.el.removeClass([this.invalidClass, 'is-invalid']);
11284         
11285         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286             
11287             var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289             if(feedback){
11290                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11291             }
11292             
11293         }
11294         
11295         if(this.indicator){
11296             this.indicator.removeClass('visible');
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298         }
11299         
11300         this.fireEvent('valid', this);
11301     },
11302     
11303      /**
11304      * Mark this field as valid
11305      */
11306     markValid : function()
11307     {
11308         if(!this.el  || this.preventMark){ // not rendered...
11309             return;
11310         }
11311         
11312         this.el.removeClass([this.invalidClass, this.validClass]);
11313         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314
11315         var feedback = this.el.select('.form-control-feedback', true).first();
11316             
11317         if(feedback){
11318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319         }
11320         
11321         if(this.indicator){
11322             this.indicator.removeClass('visible');
11323             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11324         }
11325         
11326         if(this.disabled){
11327             return;
11328         }
11329         
11330            
11331         if(this.allowBlank && !this.getRawValue().length){
11332             return;
11333         }
11334         if (Roo.bootstrap.version == 3) {
11335             this.el.addClass(this.validClass);
11336         } else {
11337             this.inputEl().addClass('is-valid');
11338         }
11339
11340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341             
11342             var feedback = this.el.select('.form-control-feedback', true).first();
11343             
11344             if(feedback){
11345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11347             }
11348             
11349         }
11350         
11351         this.fireEvent('valid', this);
11352     },
11353     
11354      /**
11355      * Mark this field as invalid
11356      * @param {String} msg The validation message
11357      */
11358     markInvalid : function(msg)
11359     {
11360         if(!this.el  || this.preventMark){ // not rendered
11361             return;
11362         }
11363         
11364         this.el.removeClass([this.invalidClass, this.validClass]);
11365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366         
11367         var feedback = this.el.select('.form-control-feedback', true).first();
11368             
11369         if(feedback){
11370             this.el.select('.form-control-feedback', true).first().removeClass(
11371                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11372         }
11373
11374         if(this.disabled){
11375             return;
11376         }
11377         
11378         if(this.allowBlank && !this.getRawValue().length){
11379             return;
11380         }
11381         
11382         if(this.indicator){
11383             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384             this.indicator.addClass('visible');
11385         }
11386         if (Roo.bootstrap.version == 3) {
11387             this.el.addClass(this.invalidClass);
11388         } else {
11389             this.inputEl().addClass('is-invalid');
11390         }
11391         
11392         
11393         
11394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395             
11396             var feedback = this.el.select('.form-control-feedback', true).first();
11397             
11398             if(feedback){
11399                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400                 
11401                 if(this.getValue().length || this.forceFeedback){
11402                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11403                 }
11404                 
11405             }
11406             
11407         }
11408         
11409         this.fireEvent('invalid', this, msg);
11410     },
11411     // private
11412     SafariOnKeyDown : function(event)
11413     {
11414         // this is a workaround for a password hang bug on chrome/ webkit.
11415         if (this.inputEl().dom.type != 'password') {
11416             return;
11417         }
11418         
11419         var isSelectAll = false;
11420         
11421         if(this.inputEl().dom.selectionEnd > 0){
11422             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423         }
11424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425             event.preventDefault();
11426             this.setValue('');
11427             return;
11428         }
11429         
11430         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431             
11432             event.preventDefault();
11433             // this is very hacky as keydown always get's upper case.
11434             //
11435             var cc = String.fromCharCode(event.getCharCode());
11436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11437             
11438         }
11439     },
11440     adjustWidth : function(tag, w){
11441         tag = tag.toLowerCase();
11442         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444                 if(tag == 'input'){
11445                     return w + 2;
11446                 }
11447                 if(tag == 'textarea'){
11448                     return w-2;
11449                 }
11450             }else if(Roo.isOpera){
11451                 if(tag == 'input'){
11452                     return w + 2;
11453                 }
11454                 if(tag == 'textarea'){
11455                     return w-2;
11456                 }
11457             }
11458         }
11459         return w;
11460     },
11461     
11462     setFieldLabel : function(v)
11463     {
11464         if(!this.rendered){
11465             return;
11466         }
11467         
11468         if(this.indicatorEl()){
11469             var ar = this.el.select('label > span',true);
11470             
11471             if (ar.elements.length) {
11472                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473                 this.fieldLabel = v;
11474                 return;
11475             }
11476             
11477             var br = this.el.select('label',true);
11478             
11479             if(br.elements.length) {
11480                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481                 this.fieldLabel = v;
11482                 return;
11483             }
11484             
11485             Roo.log('Cannot Found any of label > span || label in input');
11486             return;
11487         }
11488         
11489         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490         this.fieldLabel = v;
11491         
11492         
11493     }
11494 });
11495
11496  
11497 /*
11498  * - LGPL
11499  *
11500  * Input
11501  * 
11502  */
11503
11504 /**
11505  * @class Roo.bootstrap.TextArea
11506  * @extends Roo.bootstrap.Input
11507  * Bootstrap TextArea class
11508  * @cfg {Number} cols Specifies the visible width of a text area
11509  * @cfg {Number} rows Specifies the visible number of lines in a text area
11510  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512  * @cfg {string} html text
11513  * 
11514  * @constructor
11515  * Create a new TextArea
11516  * @param {Object} config The config object
11517  */
11518
11519 Roo.bootstrap.TextArea = function(config){
11520     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521    
11522 };
11523
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11525      
11526     cols : false,
11527     rows : 5,
11528     readOnly : false,
11529     warp : 'soft',
11530     resize : false,
11531     value: false,
11532     html: false,
11533     
11534     getAutoCreate : function(){
11535         
11536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537         
11538         var id = Roo.id();
11539         
11540         var cfg = {};
11541         
11542         if(this.inputType != 'hidden'){
11543             cfg.cls = 'form-group' //input-group
11544         }
11545         
11546         var input =  {
11547             tag: 'textarea',
11548             id : id,
11549             warp : this.warp,
11550             rows : this.rows,
11551             value : this.value || '',
11552             html: this.html || '',
11553             cls : 'form-control',
11554             placeholder : this.placeholder || '' 
11555             
11556         };
11557         
11558         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559             input.maxLength = this.maxLength;
11560         }
11561         
11562         if(this.resize){
11563             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564         }
11565         
11566         if(this.cols){
11567             input.cols = this.cols;
11568         }
11569         
11570         if (this.readOnly) {
11571             input.readonly = true;
11572         }
11573         
11574         if (this.name) {
11575             input.name = this.name;
11576         }
11577         
11578         if (this.size) {
11579             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580         }
11581         
11582         var settings=this;
11583         ['xs','sm','md','lg'].map(function(size){
11584             if (settings[size]) {
11585                 cfg.cls += ' col-' + size + '-' + settings[size];
11586             }
11587         });
11588         
11589         var inputblock = input;
11590         
11591         if(this.hasFeedback && !this.allowBlank){
11592             
11593             var feedback = {
11594                 tag: 'span',
11595                 cls: 'glyphicon form-control-feedback'
11596             };
11597
11598             inputblock = {
11599                 cls : 'has-feedback',
11600                 cn :  [
11601                     input,
11602                     feedback
11603                 ] 
11604             };  
11605         }
11606         
11607         
11608         if (this.before || this.after) {
11609             
11610             inputblock = {
11611                 cls : 'input-group',
11612                 cn :  [] 
11613             };
11614             if (this.before) {
11615                 inputblock.cn.push({
11616                     tag :'span',
11617                     cls : 'input-group-addon',
11618                     html : this.before
11619                 });
11620             }
11621             
11622             inputblock.cn.push(input);
11623             
11624             if(this.hasFeedback && !this.allowBlank){
11625                 inputblock.cls += ' has-feedback';
11626                 inputblock.cn.push(feedback);
11627             }
11628             
11629             if (this.after) {
11630                 inputblock.cn.push({
11631                     tag :'span',
11632                     cls : 'input-group-addon',
11633                     html : this.after
11634                 });
11635             }
11636             
11637         }
11638         
11639         if (align ==='left' && this.fieldLabel.length) {
11640             cfg.cn = [
11641                 {
11642                     tag: 'label',
11643                     'for' :  id,
11644                     cls : 'control-label',
11645                     html : this.fieldLabel
11646                 },
11647                 {
11648                     cls : "",
11649                     cn: [
11650                         inputblock
11651                     ]
11652                 }
11653
11654             ];
11655             
11656             if(this.labelWidth > 12){
11657                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658             }
11659
11660             if(this.labelWidth < 13 && this.labelmd == 0){
11661                 this.labelmd = this.labelWidth;
11662             }
11663
11664             if(this.labellg > 0){
11665                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667             }
11668
11669             if(this.labelmd > 0){
11670                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672             }
11673
11674             if(this.labelsm > 0){
11675                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677             }
11678
11679             if(this.labelxs > 0){
11680                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682             }
11683             
11684         } else if ( this.fieldLabel.length) {
11685             cfg.cn = [
11686
11687                {
11688                    tag: 'label',
11689                    //cls : 'input-group-addon',
11690                    html : this.fieldLabel
11691
11692                },
11693
11694                inputblock
11695
11696            ];
11697
11698         } else {
11699
11700             cfg.cn = [
11701
11702                 inputblock
11703
11704             ];
11705                 
11706         }
11707         
11708         if (this.disabled) {
11709             input.disabled=true;
11710         }
11711         
11712         return cfg;
11713         
11714     },
11715     /**
11716      * return the real textarea element.
11717      */
11718     inputEl: function ()
11719     {
11720         return this.el.select('textarea.form-control',true).first();
11721     },
11722     
11723     /**
11724      * Clear any invalid styles/messages for this field
11725      */
11726     clearInvalid : function()
11727     {
11728         
11729         if(!this.el || this.preventMark){ // not rendered
11730             return;
11731         }
11732         
11733         var label = this.el.select('label', true).first();
11734         var icon = this.el.select('i.fa-star', true).first();
11735         
11736         if(label && icon){
11737             icon.remove();
11738         }
11739         this.el.removeClass( this.validClass);
11740         this.inputEl().removeClass('is-invalid');
11741          
11742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743             
11744             var feedback = this.el.select('.form-control-feedback', true).first();
11745             
11746             if(feedback){
11747                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11748             }
11749             
11750         }
11751         
11752         this.fireEvent('valid', this);
11753     },
11754     
11755      /**
11756      * Mark this field as valid
11757      */
11758     markValid : function()
11759     {
11760         if(!this.el  || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         this.el.removeClass([this.invalidClass, this.validClass]);
11765         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766         
11767         var feedback = this.el.select('.form-control-feedback', true).first();
11768             
11769         if(feedback){
11770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771         }
11772
11773         if(this.disabled || this.allowBlank){
11774             return;
11775         }
11776         
11777         var label = this.el.select('label', true).first();
11778         var icon = this.el.select('i.fa-star', true).first();
11779         
11780         if(label && icon){
11781             icon.remove();
11782         }
11783         if (Roo.bootstrap.version == 3) {
11784             this.el.addClass(this.validClass);
11785         } else {
11786             this.inputEl().addClass('is-valid');
11787         }
11788         
11789         
11790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791             
11792             var feedback = this.el.select('.form-control-feedback', true).first();
11793             
11794             if(feedback){
11795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11797             }
11798             
11799         }
11800         
11801         this.fireEvent('valid', this);
11802     },
11803     
11804      /**
11805      * Mark this field as invalid
11806      * @param {String} msg The validation message
11807      */
11808     markInvalid : function(msg)
11809     {
11810         if(!this.el  || this.preventMark){ // not rendered
11811             return;
11812         }
11813         
11814         this.el.removeClass([this.invalidClass, this.validClass]);
11815         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816         
11817         var feedback = this.el.select('.form-control-feedback', true).first();
11818             
11819         if(feedback){
11820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821         }
11822
11823         if(this.disabled || this.allowBlank){
11824             return;
11825         }
11826         
11827         var label = this.el.select('label', true).first();
11828         var icon = this.el.select('i.fa-star', true).first();
11829         
11830         if(!this.getValue().length && label && !icon){
11831             this.el.createChild({
11832                 tag : 'i',
11833                 cls : 'text-danger fa fa-lg fa-star',
11834                 tooltip : 'This field is required',
11835                 style : 'margin-right:5px;'
11836             }, label, true);
11837         }
11838         
11839         if (Roo.bootstrap.version == 3) {
11840             this.el.addClass(this.invalidClass);
11841         } else {
11842             this.inputEl().addClass('is-invalid');
11843         }
11844         
11845         // fixme ... this may be depricated need to test..
11846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847             
11848             var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850             if(feedback){
11851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852                 
11853                 if(this.getValue().length || this.forceFeedback){
11854                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11855                 }
11856                 
11857             }
11858             
11859         }
11860         
11861         this.fireEvent('invalid', this, msg);
11862     }
11863 });
11864
11865  
11866 /*
11867  * - LGPL
11868  *
11869  * trigger field - base class for combo..
11870  * 
11871  */
11872  
11873 /**
11874  * @class Roo.bootstrap.TriggerField
11875  * @extends Roo.bootstrap.Input
11876  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879  * for which you can provide a custom implementation.  For example:
11880  * <pre><code>
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11884 </code></pre>
11885  *
11886  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11889  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891
11892  * @constructor
11893  * Create a new TriggerField.
11894  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895  * to the base TextField)
11896  */
11897 Roo.bootstrap.TriggerField = function(config){
11898     this.mimicing = false;
11899     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 };
11901
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11903     /**
11904      * @cfg {String} triggerClass A CSS class to apply to the trigger
11905      */
11906      /**
11907      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11908      */
11909     hideTrigger:false,
11910
11911     /**
11912      * @cfg {Boolean} removable (true|false) special filter default false
11913      */
11914     removable : false,
11915     
11916     /** @cfg {Boolean} grow @hide */
11917     /** @cfg {Number} growMin @hide */
11918     /** @cfg {Number} growMax @hide */
11919
11920     /**
11921      * @hide 
11922      * @method
11923      */
11924     autoSize: Roo.emptyFn,
11925     // private
11926     monitorTab : true,
11927     // private
11928     deferHeight : true,
11929
11930     
11931     actionMode : 'wrap',
11932     
11933     caret : false,
11934     
11935     
11936     getAutoCreate : function(){
11937        
11938         var align = this.labelAlign || this.parentLabelAlign();
11939         
11940         var id = Roo.id();
11941         
11942         var cfg = {
11943             cls: 'form-group' //input-group
11944         };
11945         
11946         
11947         var input =  {
11948             tag: 'input',
11949             id : id,
11950             type : this.inputType,
11951             cls : 'form-control',
11952             autocomplete: 'new-password',
11953             placeholder : this.placeholder || '' 
11954             
11955         };
11956         if (this.name) {
11957             input.name = this.name;
11958         }
11959         if (this.size) {
11960             input.cls += ' input-' + this.size;
11961         }
11962         
11963         if (this.disabled) {
11964             input.disabled=true;
11965         }
11966         
11967         var inputblock = input;
11968         
11969         if(this.hasFeedback && !this.allowBlank){
11970             
11971             var feedback = {
11972                 tag: 'span',
11973                 cls: 'glyphicon form-control-feedback'
11974             };
11975             
11976             if(this.removable && !this.editable  ){
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         {
11982                             tag: 'button',
11983                             html : 'x',
11984                             cls : 'roo-combo-removable-btn close'
11985                         },
11986                         feedback
11987                     ] 
11988                 };
11989             } else {
11990                 inputblock = {
11991                     cls : 'has-feedback',
11992                     cn :  [
11993                         inputblock,
11994                         feedback
11995                     ] 
11996                 };
11997             }
11998
11999         } else {
12000             if(this.removable && !this.editable ){
12001                 inputblock = {
12002                     cls : 'roo-removable',
12003                     cn :  [
12004                         inputblock,
12005                         {
12006                             tag: 'button',
12007                             html : 'x',
12008                             cls : 'roo-combo-removable-btn close'
12009                         }
12010                     ] 
12011                 };
12012             }
12013         }
12014         
12015         if (this.before || this.after) {
12016             
12017             inputblock = {
12018                 cls : 'input-group',
12019                 cn :  [] 
12020             };
12021             if (this.before) {
12022                 inputblock.cn.push({
12023                     tag :'span',
12024                     cls : 'input-group-addon input-group-prepend input-group-text',
12025                     html : this.before
12026                 });
12027             }
12028             
12029             inputblock.cn.push(input);
12030             
12031             if(this.hasFeedback && !this.allowBlank){
12032                 inputblock.cls += ' has-feedback';
12033                 inputblock.cn.push(feedback);
12034             }
12035             
12036             if (this.after) {
12037                 inputblock.cn.push({
12038                     tag :'span',
12039                     cls : 'input-group-addon input-group-append input-group-text',
12040                     html : this.after
12041                 });
12042             }
12043             
12044         };
12045         
12046       
12047         
12048         var ibwrap = inputblock;
12049         
12050         if(this.multiple){
12051             ibwrap = {
12052                 tag: 'ul',
12053                 cls: 'roo-select2-choices',
12054                 cn:[
12055                     {
12056                         tag: 'li',
12057                         cls: 'roo-select2-search-field',
12058                         cn: [
12059
12060                             inputblock
12061                         ]
12062                     }
12063                 ]
12064             };
12065                 
12066         }
12067         
12068         var combobox = {
12069             cls: 'roo-select2-container input-group',
12070             cn: [
12071                  {
12072                     tag: 'input',
12073                     type : 'hidden',
12074                     cls: 'form-hidden-field'
12075                 },
12076                 ibwrap
12077             ]
12078         };
12079         
12080         if(!this.multiple && this.showToggleBtn){
12081             
12082             var caret = {
12083                         tag: 'span',
12084                         cls: 'caret'
12085              };
12086             if (this.caret != false) {
12087                 caret = {
12088                      tag: 'i',
12089                      cls: 'fa fa-' + this.caret
12090                 };
12091                 
12092             }
12093             
12094             combobox.cn.push({
12095                 tag :'span',
12096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097                 cn : [
12098                     Roo.bootstrap.version == 3 ? caret : '',
12099                     {
12100                         tag: 'span',
12101                         cls: 'combobox-clear',
12102                         cn  : [
12103                             {
12104                                 tag : 'i',
12105                                 cls: 'icon-remove'
12106                             }
12107                         ]
12108                     }
12109                 ]
12110
12111             })
12112         }
12113         
12114         if(this.multiple){
12115             combobox.cls += ' roo-select2-container-multi';
12116         }
12117          var indicator = {
12118             tag : 'i',
12119             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120             tooltip : 'This field is required'
12121         };
12122         if (Roo.bootstrap.version == 4) {
12123             indicator = {
12124                 tag : 'i',
12125                 style : 'display:none'
12126             };
12127         }
12128         
12129         
12130         if (align ==='left' && this.fieldLabel.length) {
12131             
12132             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12133
12134             cfg.cn = [
12135                 indicator,
12136                 {
12137                     tag: 'label',
12138                     'for' :  id,
12139                     cls : 'control-label',
12140                     html : this.fieldLabel
12141
12142                 },
12143                 {
12144                     cls : "", 
12145                     cn: [
12146                         combobox
12147                     ]
12148                 }
12149
12150             ];
12151             
12152             var labelCfg = cfg.cn[1];
12153             var contentCfg = cfg.cn[2];
12154             
12155             if(this.indicatorpos == 'right'){
12156                 cfg.cn = [
12157                     {
12158                         tag: 'label',
12159                         'for' :  id,
12160                         cls : 'control-label',
12161                         cn : [
12162                             {
12163                                 tag : 'span',
12164                                 html : this.fieldLabel
12165                             },
12166                             indicator
12167                         ]
12168                     },
12169                     {
12170                         cls : "", 
12171                         cn: [
12172                             combobox
12173                         ]
12174                     }
12175
12176                 ];
12177                 
12178                 labelCfg = cfg.cn[0];
12179                 contentCfg = cfg.cn[1];
12180             }
12181             
12182             if(this.labelWidth > 12){
12183                 labelCfg.style = "width: " + this.labelWidth + 'px';
12184             }
12185             
12186             if(this.labelWidth < 13 && this.labelmd == 0){
12187                 this.labelmd = this.labelWidth;
12188             }
12189             
12190             if(this.labellg > 0){
12191                 labelCfg.cls += ' col-lg-' + this.labellg;
12192                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193             }
12194             
12195             if(this.labelmd > 0){
12196                 labelCfg.cls += ' col-md-' + this.labelmd;
12197                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198             }
12199             
12200             if(this.labelsm > 0){
12201                 labelCfg.cls += ' col-sm-' + this.labelsm;
12202                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203             }
12204             
12205             if(this.labelxs > 0){
12206                 labelCfg.cls += ' col-xs-' + this.labelxs;
12207                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208             }
12209             
12210         } else if ( this.fieldLabel.length) {
12211 //                Roo.log(" label");
12212             cfg.cn = [
12213                 indicator,
12214                {
12215                    tag: 'label',
12216                    //cls : 'input-group-addon',
12217                    html : this.fieldLabel
12218
12219                },
12220
12221                combobox
12222
12223             ];
12224             
12225             if(this.indicatorpos == 'right'){
12226                 
12227                 cfg.cn = [
12228                     {
12229                        tag: 'label',
12230                        cn : [
12231                            {
12232                                tag : 'span',
12233                                html : this.fieldLabel
12234                            },
12235                            indicator
12236                        ]
12237
12238                     },
12239                     combobox
12240
12241                 ];
12242
12243             }
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252         
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     
12265     
12266     // private
12267     onResize : function(w, h){
12268 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 //        if(typeof w == 'number'){
12270 //            var x = w - this.trigger.getWidth();
12271 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12272 //            this.trigger.setStyle('left', x+'px');
12273 //        }
12274     },
12275
12276     // private
12277     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278
12279     // private
12280     getResizeEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     getPositionEl : function(){
12286         return this.inputEl();
12287     },
12288
12289     // private
12290     alignErrorIcon : function(){
12291         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292     },
12293
12294     // private
12295     initEvents : function(){
12296         
12297         this.createList();
12298         
12299         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301         if(!this.multiple && this.showToggleBtn){
12302             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303             if(this.hideTrigger){
12304                 this.trigger.setDisplayed(false);
12305             }
12306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307         }
12308         
12309         if(this.multiple){
12310             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311         }
12312         
12313         if(this.removable && !this.editable && !this.tickable){
12314             var close = this.closeTriggerEl();
12315             
12316             if(close){
12317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318                 close.on('click', this.removeBtnClick, this, close);
12319             }
12320         }
12321         
12322         //this.trigger.addClassOnOver('x-form-trigger-over');
12323         //this.trigger.addClassOnClick('x-form-trigger-click');
12324         
12325         //if(!this.width){
12326         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327         //}
12328     },
12329     
12330     closeTriggerEl : function()
12331     {
12332         var close = this.el.select('.roo-combo-removable-btn', true).first();
12333         return close ? close : false;
12334     },
12335     
12336     removeBtnClick : function(e, h, el)
12337     {
12338         e.preventDefault();
12339         
12340         if(this.fireEvent("remove", this) !== false){
12341             this.reset();
12342             this.fireEvent("afterremove", this)
12343         }
12344     },
12345     
12346     createList : function()
12347     {
12348         this.list = Roo.get(document.body).createChild({
12349             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350             cls: 'typeahead typeahead-long dropdown-menu shadow',
12351             style: 'display:none'
12352         });
12353         
12354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12355         
12356     },
12357
12358     // private
12359     initTrigger : function(){
12360        
12361     },
12362
12363     // private
12364     onDestroy : function(){
12365         if(this.trigger){
12366             this.trigger.removeAllListeners();
12367           //  this.trigger.remove();
12368         }
12369         //if(this.wrap){
12370         //    this.wrap.remove();
12371         //}
12372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373     },
12374
12375     // private
12376     onFocus : function(){
12377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378         /*
12379         if(!this.mimicing){
12380             this.wrap.addClass('x-trigger-wrap-focus');
12381             this.mimicing = true;
12382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383             if(this.monitorTab){
12384                 this.el.on("keydown", this.checkTab, this);
12385             }
12386         }
12387         */
12388     },
12389
12390     // private
12391     checkTab : function(e){
12392         if(e.getKey() == e.TAB){
12393             this.triggerBlur();
12394         }
12395     },
12396
12397     // private
12398     onBlur : function(){
12399         // do nothing
12400     },
12401
12402     // private
12403     mimicBlur : function(e, t){
12404         /*
12405         if(!this.wrap.contains(t) && this.validateBlur()){
12406             this.triggerBlur();
12407         }
12408         */
12409     },
12410
12411     // private
12412     triggerBlur : function(){
12413         this.mimicing = false;
12414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415         if(this.monitorTab){
12416             this.el.un("keydown", this.checkTab, this);
12417         }
12418         //this.wrap.removeClass('x-trigger-wrap-focus');
12419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420     },
12421
12422     // private
12423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424     validateBlur : function(e, t){
12425         return true;
12426     },
12427
12428     // private
12429     onDisable : function(){
12430         this.inputEl().dom.disabled = true;
12431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432         //if(this.wrap){
12433         //    this.wrap.addClass('x-item-disabled');
12434         //}
12435     },
12436
12437     // private
12438     onEnable : function(){
12439         this.inputEl().dom.disabled = false;
12440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441         //if(this.wrap){
12442         //    this.el.removeClass('x-item-disabled');
12443         //}
12444     },
12445
12446     // private
12447     onShow : function(){
12448         var ae = this.getActionEl();
12449         
12450         if(ae){
12451             ae.dom.style.display = '';
12452             ae.dom.style.visibility = 'visible';
12453         }
12454     },
12455
12456     // private
12457     
12458     onHide : function(){
12459         var ae = this.getActionEl();
12460         ae.dom.style.display = 'none';
12461     },
12462
12463     /**
12464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12465      * by an implementing function.
12466      * @method
12467      * @param {EventObject} e
12468      */
12469     onTriggerClick : Roo.emptyFn
12470 });
12471  
12472 /*
12473 * Licence: LGPL
12474 */
12475
12476 /**
12477  * @class Roo.bootstrap.CardUploader
12478  * @extends Roo.bootstrap.Button
12479  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480  * @cfg {Number} errorTimeout default 3000
12481  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12482  * @cfg {Array}  html The button text.
12483
12484  *
12485  * @constructor
12486  * Create a new CardUploader
12487  * @param {Object} config The config object
12488  */
12489
12490 Roo.bootstrap.CardUploader = function(config){
12491     
12492  
12493     
12494     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495     
12496     
12497     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12498         return r.data.id
12499         });
12500     
12501     
12502 };
12503
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12505     
12506      
12507     errorTimeout : 3000,
12508      
12509     images : false,
12510    
12511     fileCollection : false,
12512     allowBlank : true,
12513     
12514     getAutoCreate : function()
12515     {
12516         
12517         var cfg =  {
12518             cls :'form-group' ,
12519             cn : [
12520                
12521                 {
12522                     tag: 'label',
12523                    //cls : 'input-group-addon',
12524                     html : this.fieldLabel
12525
12526                 },
12527
12528                 {
12529                     tag: 'input',
12530                     type : 'hidden',
12531                     value : this.value,
12532                     cls : 'd-none  form-control'
12533                 },
12534                 
12535                 {
12536                     tag: 'input',
12537                     multiple : 'multiple',
12538                     type : 'file',
12539                     cls : 'd-none  roo-card-upload-selector'
12540                 },
12541                 
12542                 {
12543                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12544                 },
12545                 {
12546                     cls : 'card-columns roo-card-uploader-container'
12547                 }
12548
12549             ]
12550         };
12551            
12552          
12553         return cfg;
12554     },
12555     
12556     getChildContainer : function() /// what children are added to.
12557     {
12558         return this.containerEl;
12559     },
12560    
12561     getButtonContainer : function() /// what children are added to.
12562     {
12563         return this.el.select(".roo-card-uploader-button-container").first();
12564     },
12565    
12566     initEvents : function()
12567     {
12568         
12569         Roo.bootstrap.Input.prototype.initEvents.call(this);
12570         
12571         var t = this;
12572         this.addxtype({
12573             xns: Roo.bootstrap,
12574
12575             xtype : 'Button',
12576             container_method : 'getButtonContainer' ,            
12577             html :  this.html, // fix changable?
12578             cls : 'w-100 ',
12579             listeners : {
12580                 'click' : function(btn, e) {
12581                     t.onClick(e);
12582                 }
12583             }
12584         });
12585         
12586         
12587         
12588         
12589         this.urlAPI = (window.createObjectURL && window) || 
12590                                 (window.URL && URL.revokeObjectURL && URL) || 
12591                                 (window.webkitURL && webkitURL);
12592                         
12593          
12594          
12595          
12596         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12597         
12598         this.selectorEl.on('change', this.onFileSelected, this);
12599         if (this.images) {
12600             var t = this;
12601             this.images.forEach(function(img) {
12602                 t.addCard(img)
12603             });
12604             this.images = false;
12605         }
12606         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12607          
12608        
12609     },
12610     
12611    
12612     onClick : function(e)
12613     {
12614         e.preventDefault();
12615          
12616         this.selectorEl.dom.click();
12617          
12618     },
12619     
12620     onFileSelected : function(e)
12621     {
12622         e.preventDefault();
12623         
12624         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12625             return;
12626         }
12627         
12628         Roo.each(this.selectorEl.dom.files, function(file){    
12629             this.addFile(file);
12630         }, this);
12631          
12632     },
12633     
12634       
12635     
12636       
12637     
12638     addFile : function(file)
12639     {
12640            
12641         if(typeof(file) === 'string'){
12642             throw "Add file by name?"; // should not happen
12643             return;
12644         }
12645         
12646         if(!file || !this.urlAPI){
12647             return;
12648         }
12649         
12650         // file;
12651         // file.type;
12652         
12653         var _this = this;
12654         
12655         
12656         var url = _this.urlAPI.createObjectURL( file);
12657            
12658         this.addCard({
12659             id : Roo.bootstrap.CardUploader.ID--,
12660             is_uploaded : false,
12661             src : url,
12662             title : file.name,
12663             mimetype : file.type,
12664             preview : false,
12665             is_deleted : 0
12666         })
12667         
12668     },
12669     
12670     addCard : function (data)
12671     {
12672         // hidden input element?
12673         // if the file is not an image...
12674         //then we need to use something other that and header_image
12675         var t = this;
12676         //   remove.....
12677         var footer = [
12678             {
12679                 xns : Roo.bootstrap,
12680                 xtype : 'CardFooter',
12681                 items: [
12682                     {
12683                         xns : Roo.bootstrap,
12684                         xtype : 'Element',
12685                         cls : 'd-flex',
12686                         items : [
12687                             
12688                             {
12689                                 xns : Roo.bootstrap,
12690                                 xtype : 'Button',
12691                                 html : String.format("<small>{0}</small>", data.title),
12692                                 cls : 'col-11 text-left',
12693                                 size: 'sm',
12694                                 weight: 'link',
12695                                 fa : 'download',
12696                                 listeners : {
12697                                     click : function() {
12698                                         this.downloadCard(data.id)
12699                                     }
12700                                 }
12701                             },
12702                           
12703                             {
12704                                 xns : Roo.bootstrap,
12705                                 xtype : 'Button',
12706                                 
12707                                 size : 'sm',
12708                                 weight: 'danger',
12709                                 cls : 'col-1',
12710                                 fa : 'times',
12711                                 listeners : {
12712                                     click : function() {
12713                                         t.removeCard(data.id)
12714                                     }
12715                                 }
12716                             }
12717                         ]
12718                     }
12719                     
12720                 ] 
12721             }
12722             
12723         ];
12724
12725         var cn = this.addxtype(
12726             {
12727                  
12728                 xns : Roo.bootstrap,
12729                 xtype : 'Card',
12730                 closeable : true,
12731                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12732                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12733                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12734                 data : data,
12735                 html : false,
12736                  
12737                 items : footer,
12738                 initEvents : function() {
12739                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12740                     this.imgEl = this.el.select('.card-img-top').first();
12741                     if (this.imgEl) {
12742                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12743                         this.imgEl.set({ 'pointer' : 'cursor' });
12744                                   
12745                     }
12746                     
12747                   
12748                 }
12749                 
12750             }
12751         );
12752         // dont' really need ot update items.
12753         // this.items.push(cn);
12754         this.fileCollection.add(cn);
12755         this.updateInput();
12756         
12757     },
12758     removeCard : function(id)
12759     {
12760         
12761         var card  = this.fileCollection.get(id);
12762         card.data.is_deleted = 1;
12763         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12764         this.fileCollection.remove(card);
12765         //this.items = this.items.filter(function(e) { return e != card });
12766         // dont' really need ot update items.
12767         card.el.dom.parentNode.removeChild(card.el.dom);
12768         
12769     },
12770     reset: function()
12771     {
12772         this.fileCollection.each(function(card) {
12773             card.el.dom.parentNode.removeChild(card.el.dom);    
12774         });
12775         this.fileCollection.clear();
12776         this.updateInput();
12777     },
12778     
12779     updateInput : function()
12780     {
12781         var data = [];
12782         this.fileCollection.each(function(e) {
12783             data.push(e.data);
12784         });
12785         
12786         this.inputEl().dom.value = JSON.stringify(data);
12787     }
12788     
12789     
12790 });
12791
12792
12793 Roo.bootstrap.CardUploader.ID = -1;/*
12794  * Based on:
12795  * Ext JS Library 1.1.1
12796  * Copyright(c) 2006-2007, Ext JS, LLC.
12797  *
12798  * Originally Released Under LGPL - original licence link has changed is not relivant.
12799  *
12800  * Fork - LGPL
12801  * <script type="text/javascript">
12802  */
12803
12804
12805 /**
12806  * @class Roo.data.SortTypes
12807  * @singleton
12808  * Defines the default sorting (casting?) comparison functions used when sorting data.
12809  */
12810 Roo.data.SortTypes = {
12811     /**
12812      * Default sort that does nothing
12813      * @param {Mixed} s The value being converted
12814      * @return {Mixed} The comparison value
12815      */
12816     none : function(s){
12817         return s;
12818     },
12819     
12820     /**
12821      * The regular expression used to strip tags
12822      * @type {RegExp}
12823      * @property
12824      */
12825     stripTagsRE : /<\/?[^>]+>/gi,
12826     
12827     /**
12828      * Strips all HTML tags to sort on text only
12829      * @param {Mixed} s The value being converted
12830      * @return {String} The comparison value
12831      */
12832     asText : function(s){
12833         return String(s).replace(this.stripTagsRE, "");
12834     },
12835     
12836     /**
12837      * Strips all HTML tags to sort on text only - Case insensitive
12838      * @param {Mixed} s The value being converted
12839      * @return {String} The comparison value
12840      */
12841     asUCText : function(s){
12842         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12843     },
12844     
12845     /**
12846      * Case insensitive string
12847      * @param {Mixed} s The value being converted
12848      * @return {String} The comparison value
12849      */
12850     asUCString : function(s) {
12851         return String(s).toUpperCase();
12852     },
12853     
12854     /**
12855      * Date sorting
12856      * @param {Mixed} s The value being converted
12857      * @return {Number} The comparison value
12858      */
12859     asDate : function(s) {
12860         if(!s){
12861             return 0;
12862         }
12863         if(s instanceof Date){
12864             return s.getTime();
12865         }
12866         return Date.parse(String(s));
12867     },
12868     
12869     /**
12870      * Float sorting
12871      * @param {Mixed} s The value being converted
12872      * @return {Float} The comparison value
12873      */
12874     asFloat : function(s) {
12875         var val = parseFloat(String(s).replace(/,/g, ""));
12876         if(isNaN(val)) {
12877             val = 0;
12878         }
12879         return val;
12880     },
12881     
12882     /**
12883      * Integer sorting
12884      * @param {Mixed} s The value being converted
12885      * @return {Number} The comparison value
12886      */
12887     asInt : function(s) {
12888         var val = parseInt(String(s).replace(/,/g, ""));
12889         if(isNaN(val)) {
12890             val = 0;
12891         }
12892         return val;
12893     }
12894 };/*
12895  * Based on:
12896  * Ext JS Library 1.1.1
12897  * Copyright(c) 2006-2007, Ext JS, LLC.
12898  *
12899  * Originally Released Under LGPL - original licence link has changed is not relivant.
12900  *
12901  * Fork - LGPL
12902  * <script type="text/javascript">
12903  */
12904
12905 /**
12906 * @class Roo.data.Record
12907  * Instances of this class encapsulate both record <em>definition</em> information, and record
12908  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12909  * to access Records cached in an {@link Roo.data.Store} object.<br>
12910  * <p>
12911  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12912  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12913  * objects.<br>
12914  * <p>
12915  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12916  * @constructor
12917  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12918  * {@link #create}. The parameters are the same.
12919  * @param {Array} data An associative Array of data values keyed by the field name.
12920  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12921  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12922  * not specified an integer id is generated.
12923  */
12924 Roo.data.Record = function(data, id){
12925     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12926     this.data = data;
12927 };
12928
12929 /**
12930  * Generate a constructor for a specific record layout.
12931  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12932  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12933  * Each field definition object may contain the following properties: <ul>
12934  * <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,
12935  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12936  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12937  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12938  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12939  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12940  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12941  * this may be omitted.</p></li>
12942  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12943  * <ul><li>auto (Default, implies no conversion)</li>
12944  * <li>string</li>
12945  * <li>int</li>
12946  * <li>float</li>
12947  * <li>boolean</li>
12948  * <li>date</li></ul></p></li>
12949  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12950  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12951  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12952  * by the Reader into an object that will be stored in the Record. It is passed the
12953  * following parameters:<ul>
12954  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12955  * </ul></p></li>
12956  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12957  * </ul>
12958  * <br>usage:<br><pre><code>
12959 var TopicRecord = Roo.data.Record.create(
12960     {name: 'title', mapping: 'topic_title'},
12961     {name: 'author', mapping: 'username'},
12962     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12963     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12964     {name: 'lastPoster', mapping: 'user2'},
12965     {name: 'excerpt', mapping: 'post_text'}
12966 );
12967
12968 var myNewRecord = new TopicRecord({
12969     title: 'Do my job please',
12970     author: 'noobie',
12971     totalPosts: 1,
12972     lastPost: new Date(),
12973     lastPoster: 'Animal',
12974     excerpt: 'No way dude!'
12975 });
12976 myStore.add(myNewRecord);
12977 </code></pre>
12978  * @method create
12979  * @static
12980  */
12981 Roo.data.Record.create = function(o){
12982     var f = function(){
12983         f.superclass.constructor.apply(this, arguments);
12984     };
12985     Roo.extend(f, Roo.data.Record);
12986     var p = f.prototype;
12987     p.fields = new Roo.util.MixedCollection(false, function(field){
12988         return field.name;
12989     });
12990     for(var i = 0, len = o.length; i < len; i++){
12991         p.fields.add(new Roo.data.Field(o[i]));
12992     }
12993     f.getField = function(name){
12994         return p.fields.get(name);  
12995     };
12996     return f;
12997 };
12998
12999 Roo.data.Record.AUTO_ID = 1000;
13000 Roo.data.Record.EDIT = 'edit';
13001 Roo.data.Record.REJECT = 'reject';
13002 Roo.data.Record.COMMIT = 'commit';
13003
13004 Roo.data.Record.prototype = {
13005     /**
13006      * Readonly flag - true if this record has been modified.
13007      * @type Boolean
13008      */
13009     dirty : false,
13010     editing : false,
13011     error: null,
13012     modified: null,
13013
13014     // private
13015     join : function(store){
13016         this.store = store;
13017     },
13018
13019     /**
13020      * Set the named field to the specified value.
13021      * @param {String} name The name of the field to set.
13022      * @param {Object} value The value to set the field to.
13023      */
13024     set : function(name, value){
13025         if(this.data[name] == value){
13026             return;
13027         }
13028         this.dirty = true;
13029         if(!this.modified){
13030             this.modified = {};
13031         }
13032         if(typeof this.modified[name] == 'undefined'){
13033             this.modified[name] = this.data[name];
13034         }
13035         this.data[name] = value;
13036         if(!this.editing && this.store){
13037             this.store.afterEdit(this);
13038         }       
13039     },
13040
13041     /**
13042      * Get the value of the named field.
13043      * @param {String} name The name of the field to get the value of.
13044      * @return {Object} The value of the field.
13045      */
13046     get : function(name){
13047         return this.data[name]; 
13048     },
13049
13050     // private
13051     beginEdit : function(){
13052         this.editing = true;
13053         this.modified = {}; 
13054     },
13055
13056     // private
13057     cancelEdit : function(){
13058         this.editing = false;
13059         delete this.modified;
13060     },
13061
13062     // private
13063     endEdit : function(){
13064         this.editing = false;
13065         if(this.dirty && this.store){
13066             this.store.afterEdit(this);
13067         }
13068     },
13069
13070     /**
13071      * Usually called by the {@link Roo.data.Store} which owns the Record.
13072      * Rejects all changes made to the Record since either creation, or the last commit operation.
13073      * Modified fields are reverted to their original values.
13074      * <p>
13075      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13076      * of reject operations.
13077      */
13078     reject : function(){
13079         var m = this.modified;
13080         for(var n in m){
13081             if(typeof m[n] != "function"){
13082                 this.data[n] = m[n];
13083             }
13084         }
13085         this.dirty = false;
13086         delete this.modified;
13087         this.editing = false;
13088         if(this.store){
13089             this.store.afterReject(this);
13090         }
13091     },
13092
13093     /**
13094      * Usually called by the {@link Roo.data.Store} which owns the Record.
13095      * Commits all changes made to the Record since either creation, or the last commit operation.
13096      * <p>
13097      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13098      * of commit operations.
13099      */
13100     commit : function(){
13101         this.dirty = false;
13102         delete this.modified;
13103         this.editing = false;
13104         if(this.store){
13105             this.store.afterCommit(this);
13106         }
13107     },
13108
13109     // private
13110     hasError : function(){
13111         return this.error != null;
13112     },
13113
13114     // private
13115     clearError : function(){
13116         this.error = null;
13117     },
13118
13119     /**
13120      * Creates a copy of this record.
13121      * @param {String} id (optional) A new record id if you don't want to use this record's id
13122      * @return {Record}
13123      */
13124     copy : function(newId) {
13125         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13126     }
13127 };/*
13128  * Based on:
13129  * Ext JS Library 1.1.1
13130  * Copyright(c) 2006-2007, Ext JS, LLC.
13131  *
13132  * Originally Released Under LGPL - original licence link has changed is not relivant.
13133  *
13134  * Fork - LGPL
13135  * <script type="text/javascript">
13136  */
13137
13138
13139
13140 /**
13141  * @class Roo.data.Store
13142  * @extends Roo.util.Observable
13143  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13144  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13145  * <p>
13146  * 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
13147  * has no knowledge of the format of the data returned by the Proxy.<br>
13148  * <p>
13149  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13150  * instances from the data object. These records are cached and made available through accessor functions.
13151  * @constructor
13152  * Creates a new Store.
13153  * @param {Object} config A config object containing the objects needed for the Store to access data,
13154  * and read the data into Records.
13155  */
13156 Roo.data.Store = function(config){
13157     this.data = new Roo.util.MixedCollection(false);
13158     this.data.getKey = function(o){
13159         return o.id;
13160     };
13161     this.baseParams = {};
13162     // private
13163     this.paramNames = {
13164         "start" : "start",
13165         "limit" : "limit",
13166         "sort" : "sort",
13167         "dir" : "dir",
13168         "multisort" : "_multisort"
13169     };
13170
13171     if(config && config.data){
13172         this.inlineData = config.data;
13173         delete config.data;
13174     }
13175
13176     Roo.apply(this, config);
13177     
13178     if(this.reader){ // reader passed
13179         this.reader = Roo.factory(this.reader, Roo.data);
13180         this.reader.xmodule = this.xmodule || false;
13181         if(!this.recordType){
13182             this.recordType = this.reader.recordType;
13183         }
13184         if(this.reader.onMetaChange){
13185             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13186         }
13187     }
13188
13189     if(this.recordType){
13190         this.fields = this.recordType.prototype.fields;
13191     }
13192     this.modified = [];
13193
13194     this.addEvents({
13195         /**
13196          * @event datachanged
13197          * Fires when the data cache has changed, and a widget which is using this Store
13198          * as a Record cache should refresh its view.
13199          * @param {Store} this
13200          */
13201         datachanged : true,
13202         /**
13203          * @event metachange
13204          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13205          * @param {Store} this
13206          * @param {Object} meta The JSON metadata
13207          */
13208         metachange : true,
13209         /**
13210          * @event add
13211          * Fires when Records have been added to the Store
13212          * @param {Store} this
13213          * @param {Roo.data.Record[]} records The array of Records added
13214          * @param {Number} index The index at which the record(s) were added
13215          */
13216         add : true,
13217         /**
13218          * @event remove
13219          * Fires when a Record has been removed from the Store
13220          * @param {Store} this
13221          * @param {Roo.data.Record} record The Record that was removed
13222          * @param {Number} index The index at which the record was removed
13223          */
13224         remove : true,
13225         /**
13226          * @event update
13227          * Fires when a Record has been updated
13228          * @param {Store} this
13229          * @param {Roo.data.Record} record The Record that was updated
13230          * @param {String} operation The update operation being performed.  Value may be one of:
13231          * <pre><code>
13232  Roo.data.Record.EDIT
13233  Roo.data.Record.REJECT
13234  Roo.data.Record.COMMIT
13235          * </code></pre>
13236          */
13237         update : true,
13238         /**
13239          * @event clear
13240          * Fires when the data cache has been cleared.
13241          * @param {Store} this
13242          */
13243         clear : true,
13244         /**
13245          * @event beforeload
13246          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13247          * the load action will be canceled.
13248          * @param {Store} this
13249          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13250          */
13251         beforeload : true,
13252         /**
13253          * @event beforeloadadd
13254          * Fires after a new set of Records has been loaded.
13255          * @param {Store} this
13256          * @param {Roo.data.Record[]} records The Records that were loaded
13257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13258          */
13259         beforeloadadd : true,
13260         /**
13261          * @event load
13262          * Fires after a new set of Records has been loaded, before they are added to the store.
13263          * @param {Store} this
13264          * @param {Roo.data.Record[]} records The Records that were loaded
13265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13266          * @params {Object} return from reader
13267          */
13268         load : true,
13269         /**
13270          * @event loadexception
13271          * Fires if an exception occurs in the Proxy during loading.
13272          * Called with the signature of the Proxy's "loadexception" event.
13273          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13274          * 
13275          * @param {Proxy} 
13276          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13277          * @param {Object} load options 
13278          * @param {Object} jsonData from your request (normally this contains the Exception)
13279          */
13280         loadexception : true
13281     });
13282     
13283     if(this.proxy){
13284         this.proxy = Roo.factory(this.proxy, Roo.data);
13285         this.proxy.xmodule = this.xmodule || false;
13286         this.relayEvents(this.proxy,  ["loadexception"]);
13287     }
13288     this.sortToggle = {};
13289     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13290
13291     Roo.data.Store.superclass.constructor.call(this);
13292
13293     if(this.inlineData){
13294         this.loadData(this.inlineData);
13295         delete this.inlineData;
13296     }
13297 };
13298
13299 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13300      /**
13301     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13302     * without a remote query - used by combo/forms at present.
13303     */
13304     
13305     /**
13306     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13307     */
13308     /**
13309     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13310     */
13311     /**
13312     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13313     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13314     */
13315     /**
13316     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13317     * on any HTTP request
13318     */
13319     /**
13320     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13321     */
13322     /**
13323     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13324     */
13325     multiSort: false,
13326     /**
13327     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13328     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13329     */
13330     remoteSort : false,
13331
13332     /**
13333     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13334      * loaded or when a record is removed. (defaults to false).
13335     */
13336     pruneModifiedRecords : false,
13337
13338     // private
13339     lastOptions : null,
13340
13341     /**
13342      * Add Records to the Store and fires the add event.
13343      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13344      */
13345     add : function(records){
13346         records = [].concat(records);
13347         for(var i = 0, len = records.length; i < len; i++){
13348             records[i].join(this);
13349         }
13350         var index = this.data.length;
13351         this.data.addAll(records);
13352         this.fireEvent("add", this, records, index);
13353     },
13354
13355     /**
13356      * Remove a Record from the Store and fires the remove event.
13357      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13358      */
13359     remove : function(record){
13360         var index = this.data.indexOf(record);
13361         this.data.removeAt(index);
13362  
13363         if(this.pruneModifiedRecords){
13364             this.modified.remove(record);
13365         }
13366         this.fireEvent("remove", this, record, index);
13367     },
13368
13369     /**
13370      * Remove all Records from the Store and fires the clear event.
13371      */
13372     removeAll : function(){
13373         this.data.clear();
13374         if(this.pruneModifiedRecords){
13375             this.modified = [];
13376         }
13377         this.fireEvent("clear", this);
13378     },
13379
13380     /**
13381      * Inserts Records to the Store at the given index and fires the add event.
13382      * @param {Number} index The start index at which to insert the passed Records.
13383      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13384      */
13385     insert : function(index, records){
13386         records = [].concat(records);
13387         for(var i = 0, len = records.length; i < len; i++){
13388             this.data.insert(index, records[i]);
13389             records[i].join(this);
13390         }
13391         this.fireEvent("add", this, records, index);
13392     },
13393
13394     /**
13395      * Get the index within the cache of the passed Record.
13396      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13397      * @return {Number} The index of the passed Record. Returns -1 if not found.
13398      */
13399     indexOf : function(record){
13400         return this.data.indexOf(record);
13401     },
13402
13403     /**
13404      * Get the index within the cache of the Record with the passed id.
13405      * @param {String} id The id of the Record to find.
13406      * @return {Number} The index of the Record. Returns -1 if not found.
13407      */
13408     indexOfId : function(id){
13409         return this.data.indexOfKey(id);
13410     },
13411
13412     /**
13413      * Get the Record with the specified id.
13414      * @param {String} id The id of the Record to find.
13415      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13416      */
13417     getById : function(id){
13418         return this.data.key(id);
13419     },
13420
13421     /**
13422      * Get the Record at the specified index.
13423      * @param {Number} index The index of the Record to find.
13424      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13425      */
13426     getAt : function(index){
13427         return this.data.itemAt(index);
13428     },
13429
13430     /**
13431      * Returns a range of Records between specified indices.
13432      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13433      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13434      * @return {Roo.data.Record[]} An array of Records
13435      */
13436     getRange : function(start, end){
13437         return this.data.getRange(start, end);
13438     },
13439
13440     // private
13441     storeOptions : function(o){
13442         o = Roo.apply({}, o);
13443         delete o.callback;
13444         delete o.scope;
13445         this.lastOptions = o;
13446     },
13447
13448     /**
13449      * Loads the Record cache from the configured Proxy using the configured Reader.
13450      * <p>
13451      * If using remote paging, then the first load call must specify the <em>start</em>
13452      * and <em>limit</em> properties in the options.params property to establish the initial
13453      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13454      * <p>
13455      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13456      * and this call will return before the new data has been loaded. Perform any post-processing
13457      * in a callback function, or in a "load" event handler.</strong>
13458      * <p>
13459      * @param {Object} options An object containing properties which control loading options:<ul>
13460      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13461      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13462      * passed the following arguments:<ul>
13463      * <li>r : Roo.data.Record[]</li>
13464      * <li>options: Options object from the load call</li>
13465      * <li>success: Boolean success indicator</li></ul></li>
13466      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13467      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13468      * </ul>
13469      */
13470     load : function(options){
13471         options = options || {};
13472         if(this.fireEvent("beforeload", this, options) !== false){
13473             this.storeOptions(options);
13474             var p = Roo.apply(options.params || {}, this.baseParams);
13475             // if meta was not loaded from remote source.. try requesting it.
13476             if (!this.reader.metaFromRemote) {
13477                 p._requestMeta = 1;
13478             }
13479             if(this.sortInfo && this.remoteSort){
13480                 var pn = this.paramNames;
13481                 p[pn["sort"]] = this.sortInfo.field;
13482                 p[pn["dir"]] = this.sortInfo.direction;
13483             }
13484             if (this.multiSort) {
13485                 var pn = this.paramNames;
13486                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13487             }
13488             
13489             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13490         }
13491     },
13492
13493     /**
13494      * Reloads the Record cache from the configured Proxy using the configured Reader and
13495      * the options from the last load operation performed.
13496      * @param {Object} options (optional) An object containing properties which may override the options
13497      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13498      * the most recently used options are reused).
13499      */
13500     reload : function(options){
13501         this.load(Roo.applyIf(options||{}, this.lastOptions));
13502     },
13503
13504     // private
13505     // Called as a callback by the Reader during a load operation.
13506     loadRecords : function(o, options, success){
13507         if(!o || success === false){
13508             if(success !== false){
13509                 this.fireEvent("load", this, [], options, o);
13510             }
13511             if(options.callback){
13512                 options.callback.call(options.scope || this, [], options, false);
13513             }
13514             return;
13515         }
13516         // if data returned failure - throw an exception.
13517         if (o.success === false) {
13518             // show a message if no listener is registered.
13519             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13520                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13521             }
13522             // loadmask wil be hooked into this..
13523             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13524             return;
13525         }
13526         var r = o.records, t = o.totalRecords || r.length;
13527         
13528         this.fireEvent("beforeloadadd", this, r, options, o);
13529         
13530         if(!options || options.add !== true){
13531             if(this.pruneModifiedRecords){
13532                 this.modified = [];
13533             }
13534             for(var i = 0, len = r.length; i < len; i++){
13535                 r[i].join(this);
13536             }
13537             if(this.snapshot){
13538                 this.data = this.snapshot;
13539                 delete this.snapshot;
13540             }
13541             this.data.clear();
13542             this.data.addAll(r);
13543             this.totalLength = t;
13544             this.applySort();
13545             this.fireEvent("datachanged", this);
13546         }else{
13547             this.totalLength = Math.max(t, this.data.length+r.length);
13548             this.add(r);
13549         }
13550         
13551         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13552                 
13553             var e = new Roo.data.Record({});
13554
13555             e.set(this.parent.displayField, this.parent.emptyTitle);
13556             e.set(this.parent.valueField, '');
13557
13558             this.insert(0, e);
13559         }
13560             
13561         this.fireEvent("load", this, r, options, o);
13562         if(options.callback){
13563             options.callback.call(options.scope || this, r, options, true);
13564         }
13565     },
13566
13567
13568     /**
13569      * Loads data from a passed data block. A Reader which understands the format of the data
13570      * must have been configured in the constructor.
13571      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13572      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13573      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13574      */
13575     loadData : function(o, append){
13576         var r = this.reader.readRecords(o);
13577         this.loadRecords(r, {add: append}, true);
13578     },
13579     
13580      /**
13581      * using 'cn' the nested child reader read the child array into it's child stores.
13582      * @param {Object} rec The record with a 'children array
13583      */
13584     loadDataFromChildren : function(rec)
13585     {
13586         this.loadData(this.reader.toLoadData(rec));
13587     },
13588     
13589
13590     /**
13591      * Gets the number of cached records.
13592      * <p>
13593      * <em>If using paging, this may not be the total size of the dataset. If the data object
13594      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13595      * the data set size</em>
13596      */
13597     getCount : function(){
13598         return this.data.length || 0;
13599     },
13600
13601     /**
13602      * Gets the total number of records in the dataset as returned by the server.
13603      * <p>
13604      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13605      * the dataset size</em>
13606      */
13607     getTotalCount : function(){
13608         return this.totalLength || 0;
13609     },
13610
13611     /**
13612      * Returns the sort state of the Store as an object with two properties:
13613      * <pre><code>
13614  field {String} The name of the field by which the Records are sorted
13615  direction {String} The sort order, "ASC" or "DESC"
13616      * </code></pre>
13617      */
13618     getSortState : function(){
13619         return this.sortInfo;
13620     },
13621
13622     // private
13623     applySort : function(){
13624         if(this.sortInfo && !this.remoteSort){
13625             var s = this.sortInfo, f = s.field;
13626             var st = this.fields.get(f).sortType;
13627             var fn = function(r1, r2){
13628                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13629                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13630             };
13631             this.data.sort(s.direction, fn);
13632             if(this.snapshot && this.snapshot != this.data){
13633                 this.snapshot.sort(s.direction, fn);
13634             }
13635         }
13636     },
13637
13638     /**
13639      * Sets the default sort column and order to be used by the next load operation.
13640      * @param {String} fieldName The name of the field to sort by.
13641      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13642      */
13643     setDefaultSort : function(field, dir){
13644         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13645     },
13646
13647     /**
13648      * Sort the Records.
13649      * If remote sorting is used, the sort is performed on the server, and the cache is
13650      * reloaded. If local sorting is used, the cache is sorted internally.
13651      * @param {String} fieldName The name of the field to sort by.
13652      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13653      */
13654     sort : function(fieldName, dir){
13655         var f = this.fields.get(fieldName);
13656         if(!dir){
13657             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13658             
13659             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13660                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13661             }else{
13662                 dir = f.sortDir;
13663             }
13664         }
13665         this.sortToggle[f.name] = dir;
13666         this.sortInfo = {field: f.name, direction: dir};
13667         if(!this.remoteSort){
13668             this.applySort();
13669             this.fireEvent("datachanged", this);
13670         }else{
13671             this.load(this.lastOptions);
13672         }
13673     },
13674
13675     /**
13676      * Calls the specified function for each of the Records in the cache.
13677      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13678      * Returning <em>false</em> aborts and exits the iteration.
13679      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13680      */
13681     each : function(fn, scope){
13682         this.data.each(fn, scope);
13683     },
13684
13685     /**
13686      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13687      * (e.g., during paging).
13688      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13689      */
13690     getModifiedRecords : function(){
13691         return this.modified;
13692     },
13693
13694     // private
13695     createFilterFn : function(property, value, anyMatch){
13696         if(!value.exec){ // not a regex
13697             value = String(value);
13698             if(value.length == 0){
13699                 return false;
13700             }
13701             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13702         }
13703         return function(r){
13704             return value.test(r.data[property]);
13705         };
13706     },
13707
13708     /**
13709      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13710      * @param {String} property A field on your records
13711      * @param {Number} start The record index to start at (defaults to 0)
13712      * @param {Number} end The last record index to include (defaults to length - 1)
13713      * @return {Number} The sum
13714      */
13715     sum : function(property, start, end){
13716         var rs = this.data.items, v = 0;
13717         start = start || 0;
13718         end = (end || end === 0) ? end : rs.length-1;
13719
13720         for(var i = start; i <= end; i++){
13721             v += (rs[i].data[property] || 0);
13722         }
13723         return v;
13724     },
13725
13726     /**
13727      * Filter the records by a specified property.
13728      * @param {String} field A field on your records
13729      * @param {String/RegExp} value Either a string that the field
13730      * should start with or a RegExp to test against the field
13731      * @param {Boolean} anyMatch True to match any part not just the beginning
13732      */
13733     filter : function(property, value, anyMatch){
13734         var fn = this.createFilterFn(property, value, anyMatch);
13735         return fn ? this.filterBy(fn) : this.clearFilter();
13736     },
13737
13738     /**
13739      * Filter by a function. The specified function will be called with each
13740      * record in this data source. If the function returns true the record is included,
13741      * otherwise it is filtered.
13742      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13743      * @param {Object} scope (optional) The scope of the function (defaults to this)
13744      */
13745     filterBy : function(fn, scope){
13746         this.snapshot = this.snapshot || this.data;
13747         this.data = this.queryBy(fn, scope||this);
13748         this.fireEvent("datachanged", this);
13749     },
13750
13751     /**
13752      * Query the records by a specified property.
13753      * @param {String} field A field on your records
13754      * @param {String/RegExp} value Either a string that the field
13755      * should start with or a RegExp to test against the field
13756      * @param {Boolean} anyMatch True to match any part not just the beginning
13757      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13758      */
13759     query : function(property, value, anyMatch){
13760         var fn = this.createFilterFn(property, value, anyMatch);
13761         return fn ? this.queryBy(fn) : this.data.clone();
13762     },
13763
13764     /**
13765      * Query by a function. The specified function will be called with each
13766      * record in this data source. If the function returns true the record is included
13767      * in the results.
13768      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13769      * @param {Object} scope (optional) The scope of the function (defaults to this)
13770       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13771      **/
13772     queryBy : function(fn, scope){
13773         var data = this.snapshot || this.data;
13774         return data.filterBy(fn, scope||this);
13775     },
13776
13777     /**
13778      * Collects unique values for a particular dataIndex from this store.
13779      * @param {String} dataIndex The property to collect
13780      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13781      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13782      * @return {Array} An array of the unique values
13783      **/
13784     collect : function(dataIndex, allowNull, bypassFilter){
13785         var d = (bypassFilter === true && this.snapshot) ?
13786                 this.snapshot.items : this.data.items;
13787         var v, sv, r = [], l = {};
13788         for(var i = 0, len = d.length; i < len; i++){
13789             v = d[i].data[dataIndex];
13790             sv = String(v);
13791             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13792                 l[sv] = true;
13793                 r[r.length] = v;
13794             }
13795         }
13796         return r;
13797     },
13798
13799     /**
13800      * Revert to a view of the Record cache with no filtering applied.
13801      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13802      */
13803     clearFilter : function(suppressEvent){
13804         if(this.snapshot && this.snapshot != this.data){
13805             this.data = this.snapshot;
13806             delete this.snapshot;
13807             if(suppressEvent !== true){
13808                 this.fireEvent("datachanged", this);
13809             }
13810         }
13811     },
13812
13813     // private
13814     afterEdit : function(record){
13815         if(this.modified.indexOf(record) == -1){
13816             this.modified.push(record);
13817         }
13818         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13819     },
13820     
13821     // private
13822     afterReject : function(record){
13823         this.modified.remove(record);
13824         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13825     },
13826
13827     // private
13828     afterCommit : function(record){
13829         this.modified.remove(record);
13830         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13831     },
13832
13833     /**
13834      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13835      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13836      */
13837     commitChanges : function(){
13838         var m = this.modified.slice(0);
13839         this.modified = [];
13840         for(var i = 0, len = m.length; i < len; i++){
13841             m[i].commit();
13842         }
13843     },
13844
13845     /**
13846      * Cancel outstanding changes on all changed records.
13847      */
13848     rejectChanges : function(){
13849         var m = this.modified.slice(0);
13850         this.modified = [];
13851         for(var i = 0, len = m.length; i < len; i++){
13852             m[i].reject();
13853         }
13854     },
13855
13856     onMetaChange : function(meta, rtype, o){
13857         this.recordType = rtype;
13858         this.fields = rtype.prototype.fields;
13859         delete this.snapshot;
13860         this.sortInfo = meta.sortInfo || this.sortInfo;
13861         this.modified = [];
13862         this.fireEvent('metachange', this, this.reader.meta);
13863     },
13864     
13865     moveIndex : function(data, type)
13866     {
13867         var index = this.indexOf(data);
13868         
13869         var newIndex = index + type;
13870         
13871         this.remove(data);
13872         
13873         this.insert(newIndex, data);
13874         
13875     }
13876 });/*
13877  * Based on:
13878  * Ext JS Library 1.1.1
13879  * Copyright(c) 2006-2007, Ext JS, LLC.
13880  *
13881  * Originally Released Under LGPL - original licence link has changed is not relivant.
13882  *
13883  * Fork - LGPL
13884  * <script type="text/javascript">
13885  */
13886
13887 /**
13888  * @class Roo.data.SimpleStore
13889  * @extends Roo.data.Store
13890  * Small helper class to make creating Stores from Array data easier.
13891  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13892  * @cfg {Array} fields An array of field definition objects, or field name strings.
13893  * @cfg {Object} an existing reader (eg. copied from another store)
13894  * @cfg {Array} data The multi-dimensional array of data
13895  * @constructor
13896  * @param {Object} config
13897  */
13898 Roo.data.SimpleStore = function(config)
13899 {
13900     Roo.data.SimpleStore.superclass.constructor.call(this, {
13901         isLocal : true,
13902         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13903                 id: config.id
13904             },
13905             Roo.data.Record.create(config.fields)
13906         ),
13907         proxy : new Roo.data.MemoryProxy(config.data)
13908     });
13909     this.load();
13910 };
13911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13912  * Based on:
13913  * Ext JS Library 1.1.1
13914  * Copyright(c) 2006-2007, Ext JS, LLC.
13915  *
13916  * Originally Released Under LGPL - original licence link has changed is not relivant.
13917  *
13918  * Fork - LGPL
13919  * <script type="text/javascript">
13920  */
13921
13922 /**
13923 /**
13924  * @extends Roo.data.Store
13925  * @class Roo.data.JsonStore
13926  * Small helper class to make creating Stores for JSON data easier. <br/>
13927 <pre><code>
13928 var store = new Roo.data.JsonStore({
13929     url: 'get-images.php',
13930     root: 'images',
13931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13932 });
13933 </code></pre>
13934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13935  * JsonReader and HttpProxy (unless inline data is provided).</b>
13936  * @cfg {Array} fields An array of field definition objects, or field name strings.
13937  * @constructor
13938  * @param {Object} config
13939  */
13940 Roo.data.JsonStore = function(c){
13941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13943         reader: new Roo.data.JsonReader(c, c.fields)
13944     }));
13945 };
13946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13947  * Based on:
13948  * Ext JS Library 1.1.1
13949  * Copyright(c) 2006-2007, Ext JS, LLC.
13950  *
13951  * Originally Released Under LGPL - original licence link has changed is not relivant.
13952  *
13953  * Fork - LGPL
13954  * <script type="text/javascript">
13955  */
13956
13957  
13958 Roo.data.Field = function(config){
13959     if(typeof config == "string"){
13960         config = {name: config};
13961     }
13962     Roo.apply(this, config);
13963     
13964     if(!this.type){
13965         this.type = "auto";
13966     }
13967     
13968     var st = Roo.data.SortTypes;
13969     // named sortTypes are supported, here we look them up
13970     if(typeof this.sortType == "string"){
13971         this.sortType = st[this.sortType];
13972     }
13973     
13974     // set default sortType for strings and dates
13975     if(!this.sortType){
13976         switch(this.type){
13977             case "string":
13978                 this.sortType = st.asUCString;
13979                 break;
13980             case "date":
13981                 this.sortType = st.asDate;
13982                 break;
13983             default:
13984                 this.sortType = st.none;
13985         }
13986     }
13987
13988     // define once
13989     var stripRe = /[\$,%]/g;
13990
13991     // prebuilt conversion function for this field, instead of
13992     // switching every time we're reading a value
13993     if(!this.convert){
13994         var cv, dateFormat = this.dateFormat;
13995         switch(this.type){
13996             case "":
13997             case "auto":
13998             case undefined:
13999                 cv = function(v){ return v; };
14000                 break;
14001             case "string":
14002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14003                 break;
14004             case "int":
14005                 cv = function(v){
14006                     return v !== undefined && v !== null && v !== '' ?
14007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14008                     };
14009                 break;
14010             case "float":
14011                 cv = function(v){
14012                     return v !== undefined && v !== null && v !== '' ?
14013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14014                     };
14015                 break;
14016             case "bool":
14017             case "boolean":
14018                 cv = function(v){ return v === true || v === "true" || v == 1; };
14019                 break;
14020             case "date":
14021                 cv = function(v){
14022                     if(!v){
14023                         return '';
14024                     }
14025                     if(v instanceof Date){
14026                         return v;
14027                     }
14028                     if(dateFormat){
14029                         if(dateFormat == "timestamp"){
14030                             return new Date(v*1000);
14031                         }
14032                         return Date.parseDate(v, dateFormat);
14033                     }
14034                     var parsed = Date.parse(v);
14035                     return parsed ? new Date(parsed) : null;
14036                 };
14037              break;
14038             
14039         }
14040         this.convert = cv;
14041     }
14042 };
14043
14044 Roo.data.Field.prototype = {
14045     dateFormat: null,
14046     defaultValue: "",
14047     mapping: null,
14048     sortType : null,
14049     sortDir : "ASC"
14050 };/*
14051  * Based on:
14052  * Ext JS Library 1.1.1
14053  * Copyright(c) 2006-2007, Ext JS, LLC.
14054  *
14055  * Originally Released Under LGPL - original licence link has changed is not relivant.
14056  *
14057  * Fork - LGPL
14058  * <script type="text/javascript">
14059  */
14060  
14061 // Base class for reading structured data from a data source.  This class is intended to be
14062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14063
14064 /**
14065  * @class Roo.data.DataReader
14066  * Base class for reading structured data from a data source.  This class is intended to be
14067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14068  */
14069
14070 Roo.data.DataReader = function(meta, recordType){
14071     
14072     this.meta = meta;
14073     
14074     this.recordType = recordType instanceof Array ? 
14075         Roo.data.Record.create(recordType) : recordType;
14076 };
14077
14078 Roo.data.DataReader.prototype = {
14079     
14080     
14081     readerType : 'Data',
14082      /**
14083      * Create an empty record
14084      * @param {Object} data (optional) - overlay some values
14085      * @return {Roo.data.Record} record created.
14086      */
14087     newRow :  function(d) {
14088         var da =  {};
14089         this.recordType.prototype.fields.each(function(c) {
14090             switch( c.type) {
14091                 case 'int' : da[c.name] = 0; break;
14092                 case 'date' : da[c.name] = new Date(); break;
14093                 case 'float' : da[c.name] = 0.0; break;
14094                 case 'boolean' : da[c.name] = false; break;
14095                 default : da[c.name] = ""; break;
14096             }
14097             
14098         });
14099         return new this.recordType(Roo.apply(da, d));
14100     }
14101     
14102     
14103 };/*
14104  * Based on:
14105  * Ext JS Library 1.1.1
14106  * Copyright(c) 2006-2007, Ext JS, LLC.
14107  *
14108  * Originally Released Under LGPL - original licence link has changed is not relivant.
14109  *
14110  * Fork - LGPL
14111  * <script type="text/javascript">
14112  */
14113
14114 /**
14115  * @class Roo.data.DataProxy
14116  * @extends Roo.data.Observable
14117  * This class is an abstract base class for implementations which provide retrieval of
14118  * unformatted data objects.<br>
14119  * <p>
14120  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14121  * (of the appropriate type which knows how to parse the data object) to provide a block of
14122  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14123  * <p>
14124  * Custom implementations must implement the load method as described in
14125  * {@link Roo.data.HttpProxy#load}.
14126  */
14127 Roo.data.DataProxy = function(){
14128     this.addEvents({
14129         /**
14130          * @event beforeload
14131          * Fires before a network request is made to retrieve a data object.
14132          * @param {Object} This DataProxy object.
14133          * @param {Object} params The params parameter to the load function.
14134          */
14135         beforeload : true,
14136         /**
14137          * @event load
14138          * Fires before the load method's callback is called.
14139          * @param {Object} This DataProxy object.
14140          * @param {Object} o The data object.
14141          * @param {Object} arg The callback argument object passed to the load function.
14142          */
14143         load : true,
14144         /**
14145          * @event loadexception
14146          * Fires if an Exception occurs during data retrieval.
14147          * @param {Object} This DataProxy object.
14148          * @param {Object} o The data object.
14149          * @param {Object} arg The callback argument object passed to the load function.
14150          * @param {Object} e The Exception.
14151          */
14152         loadexception : true
14153     });
14154     Roo.data.DataProxy.superclass.constructor.call(this);
14155 };
14156
14157 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14158
14159     /**
14160      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14161      */
14162 /*
14163  * Based on:
14164  * Ext JS Library 1.1.1
14165  * Copyright(c) 2006-2007, Ext JS, LLC.
14166  *
14167  * Originally Released Under LGPL - original licence link has changed is not relivant.
14168  *
14169  * Fork - LGPL
14170  * <script type="text/javascript">
14171  */
14172 /**
14173  * @class Roo.data.MemoryProxy
14174  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14175  * to the Reader when its load method is called.
14176  * @constructor
14177  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14178  */
14179 Roo.data.MemoryProxy = function(data){
14180     if (data.data) {
14181         data = data.data;
14182     }
14183     Roo.data.MemoryProxy.superclass.constructor.call(this);
14184     this.data = data;
14185 };
14186
14187 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14188     
14189     /**
14190      * Load data from the requested source (in this case an in-memory
14191      * data object passed to the constructor), read the data object into
14192      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14193      * process that block using the passed callback.
14194      * @param {Object} params This parameter is not used by the MemoryProxy class.
14195      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14196      * object into a block of Roo.data.Records.
14197      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14198      * The function must be passed <ul>
14199      * <li>The Record block object</li>
14200      * <li>The "arg" argument from the load function</li>
14201      * <li>A boolean success indicator</li>
14202      * </ul>
14203      * @param {Object} scope The scope in which to call the callback
14204      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14205      */
14206     load : function(params, reader, callback, scope, arg){
14207         params = params || {};
14208         var result;
14209         try {
14210             result = reader.readRecords(params.data ? params.data :this.data);
14211         }catch(e){
14212             this.fireEvent("loadexception", this, arg, null, e);
14213             callback.call(scope, null, arg, false);
14214             return;
14215         }
14216         callback.call(scope, result, arg, true);
14217     },
14218     
14219     // private
14220     update : function(params, records){
14221         
14222     }
14223 });/*
14224  * Based on:
14225  * Ext JS Library 1.1.1
14226  * Copyright(c) 2006-2007, Ext JS, LLC.
14227  *
14228  * Originally Released Under LGPL - original licence link has changed is not relivant.
14229  *
14230  * Fork - LGPL
14231  * <script type="text/javascript">
14232  */
14233 /**
14234  * @class Roo.data.HttpProxy
14235  * @extends Roo.data.DataProxy
14236  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14237  * configured to reference a certain URL.<br><br>
14238  * <p>
14239  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14240  * from which the running page was served.<br><br>
14241  * <p>
14242  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14243  * <p>
14244  * Be aware that to enable the browser to parse an XML document, the server must set
14245  * the Content-Type header in the HTTP response to "text/xml".
14246  * @constructor
14247  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14248  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14249  * will be used to make the request.
14250  */
14251 Roo.data.HttpProxy = function(conn){
14252     Roo.data.HttpProxy.superclass.constructor.call(this);
14253     // is conn a conn config or a real conn?
14254     this.conn = conn;
14255     this.useAjax = !conn || !conn.events;
14256   
14257 };
14258
14259 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14260     // thse are take from connection...
14261     
14262     /**
14263      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14264      */
14265     /**
14266      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14267      * extra parameters to each request made by this object. (defaults to undefined)
14268      */
14269     /**
14270      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14271      *  to each request made by this object. (defaults to undefined)
14272      */
14273     /**
14274      * @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)
14275      */
14276     /**
14277      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14278      */
14279      /**
14280      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14281      * @type Boolean
14282      */
14283   
14284
14285     /**
14286      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14287      * @type Boolean
14288      */
14289     /**
14290      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14291      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14292      * a finer-grained basis than the DataProxy events.
14293      */
14294     getConnection : function(){
14295         return this.useAjax ? Roo.Ajax : this.conn;
14296     },
14297
14298     /**
14299      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14300      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14301      * process that block using the passed callback.
14302      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14303      * for the request to the remote server.
14304      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14305      * object into a block of Roo.data.Records.
14306      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14307      * The function must be passed <ul>
14308      * <li>The Record block object</li>
14309      * <li>The "arg" argument from the load function</li>
14310      * <li>A boolean success indicator</li>
14311      * </ul>
14312      * @param {Object} scope The scope in which to call the callback
14313      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14314      */
14315     load : function(params, reader, callback, scope, arg){
14316         if(this.fireEvent("beforeload", this, params) !== false){
14317             var  o = {
14318                 params : params || {},
14319                 request: {
14320                     callback : callback,
14321                     scope : scope,
14322                     arg : arg
14323                 },
14324                 reader: reader,
14325                 callback : this.loadResponse,
14326                 scope: this
14327             };
14328             if(this.useAjax){
14329                 Roo.applyIf(o, this.conn);
14330                 if(this.activeRequest){
14331                     Roo.Ajax.abort(this.activeRequest);
14332                 }
14333                 this.activeRequest = Roo.Ajax.request(o);
14334             }else{
14335                 this.conn.request(o);
14336             }
14337         }else{
14338             callback.call(scope||this, null, arg, false);
14339         }
14340     },
14341
14342     // private
14343     loadResponse : function(o, success, response){
14344         delete this.activeRequest;
14345         if(!success){
14346             this.fireEvent("loadexception", this, o, response);
14347             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14348             return;
14349         }
14350         var result;
14351         try {
14352             result = o.reader.read(response);
14353         }catch(e){
14354             this.fireEvent("loadexception", this, o, response, e);
14355             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14356             return;
14357         }
14358         
14359         this.fireEvent("load", this, o, o.request.arg);
14360         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14361     },
14362
14363     // private
14364     update : function(dataSet){
14365
14366     },
14367
14368     // private
14369     updateResponse : function(dataSet){
14370
14371     }
14372 });/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382
14383 /**
14384  * @class Roo.data.ScriptTagProxy
14385  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14386  * other than the originating domain of the running page.<br><br>
14387  * <p>
14388  * <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
14389  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14390  * <p>
14391  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14392  * source code that is used as the source inside a &lt;script> tag.<br><br>
14393  * <p>
14394  * In order for the browser to process the returned data, the server must wrap the data object
14395  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14396  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14397  * depending on whether the callback name was passed:
14398  * <p>
14399  * <pre><code>
14400 boolean scriptTag = false;
14401 String cb = request.getParameter("callback");
14402 if (cb != null) {
14403     scriptTag = true;
14404     response.setContentType("text/javascript");
14405 } else {
14406     response.setContentType("application/x-json");
14407 }
14408 Writer out = response.getWriter();
14409 if (scriptTag) {
14410     out.write(cb + "(");
14411 }
14412 out.print(dataBlock.toJsonString());
14413 if (scriptTag) {
14414     out.write(");");
14415 }
14416 </pre></code>
14417  *
14418  * @constructor
14419  * @param {Object} config A configuration object.
14420  */
14421 Roo.data.ScriptTagProxy = function(config){
14422     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14423     Roo.apply(this, config);
14424     this.head = document.getElementsByTagName("head")[0];
14425 };
14426
14427 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14428
14429 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14430     /**
14431      * @cfg {String} url The URL from which to request the data object.
14432      */
14433     /**
14434      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14435      */
14436     timeout : 30000,
14437     /**
14438      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14439      * the server the name of the callback function set up by the load call to process the returned data object.
14440      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14441      * javascript output which calls this named function passing the data object as its only parameter.
14442      */
14443     callbackParam : "callback",
14444     /**
14445      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14446      * name to the request.
14447      */
14448     nocache : true,
14449
14450     /**
14451      * Load data from the configured URL, read the data object into
14452      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14453      * process that block using the passed callback.
14454      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14455      * for the request to the remote server.
14456      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14457      * object into a block of Roo.data.Records.
14458      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14459      * The function must be passed <ul>
14460      * <li>The Record block object</li>
14461      * <li>The "arg" argument from the load function</li>
14462      * <li>A boolean success indicator</li>
14463      * </ul>
14464      * @param {Object} scope The scope in which to call the callback
14465      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14466      */
14467     load : function(params, reader, callback, scope, arg){
14468         if(this.fireEvent("beforeload", this, params) !== false){
14469
14470             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14471
14472             var url = this.url;
14473             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14474             if(this.nocache){
14475                 url += "&_dc=" + (new Date().getTime());
14476             }
14477             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14478             var trans = {
14479                 id : transId,
14480                 cb : "stcCallback"+transId,
14481                 scriptId : "stcScript"+transId,
14482                 params : params,
14483                 arg : arg,
14484                 url : url,
14485                 callback : callback,
14486                 scope : scope,
14487                 reader : reader
14488             };
14489             var conn = this;
14490
14491             window[trans.cb] = function(o){
14492                 conn.handleResponse(o, trans);
14493             };
14494
14495             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14496
14497             if(this.autoAbort !== false){
14498                 this.abort();
14499             }
14500
14501             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14502
14503             var script = document.createElement("script");
14504             script.setAttribute("src", url);
14505             script.setAttribute("type", "text/javascript");
14506             script.setAttribute("id", trans.scriptId);
14507             this.head.appendChild(script);
14508
14509             this.trans = trans;
14510         }else{
14511             callback.call(scope||this, null, arg, false);
14512         }
14513     },
14514
14515     // private
14516     isLoading : function(){
14517         return this.trans ? true : false;
14518     },
14519
14520     /**
14521      * Abort the current server request.
14522      */
14523     abort : function(){
14524         if(this.isLoading()){
14525             this.destroyTrans(this.trans);
14526         }
14527     },
14528
14529     // private
14530     destroyTrans : function(trans, isLoaded){
14531         this.head.removeChild(document.getElementById(trans.scriptId));
14532         clearTimeout(trans.timeoutId);
14533         if(isLoaded){
14534             window[trans.cb] = undefined;
14535             try{
14536                 delete window[trans.cb];
14537             }catch(e){}
14538         }else{
14539             // if hasn't been loaded, wait for load to remove it to prevent script error
14540             window[trans.cb] = function(){
14541                 window[trans.cb] = undefined;
14542                 try{
14543                     delete window[trans.cb];
14544                 }catch(e){}
14545             };
14546         }
14547     },
14548
14549     // private
14550     handleResponse : function(o, trans){
14551         this.trans = false;
14552         this.destroyTrans(trans, true);
14553         var result;
14554         try {
14555             result = trans.reader.readRecords(o);
14556         }catch(e){
14557             this.fireEvent("loadexception", this, o, trans.arg, e);
14558             trans.callback.call(trans.scope||window, null, trans.arg, false);
14559             return;
14560         }
14561         this.fireEvent("load", this, o, trans.arg);
14562         trans.callback.call(trans.scope||window, result, trans.arg, true);
14563     },
14564
14565     // private
14566     handleFailure : function(trans){
14567         this.trans = false;
14568         this.destroyTrans(trans, false);
14569         this.fireEvent("loadexception", this, null, trans.arg);
14570         trans.callback.call(trans.scope||window, null, trans.arg, false);
14571     }
14572 });/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583 /**
14584  * @class Roo.data.JsonReader
14585  * @extends Roo.data.DataReader
14586  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14587  * based on mappings in a provided Roo.data.Record constructor.
14588  * 
14589  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14590  * in the reply previously. 
14591  * 
14592  * <p>
14593  * Example code:
14594  * <pre><code>
14595 var RecordDef = Roo.data.Record.create([
14596     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14597     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14598 ]);
14599 var myReader = new Roo.data.JsonReader({
14600     totalProperty: "results",    // The property which contains the total dataset size (optional)
14601     root: "rows",                // The property which contains an Array of row objects
14602     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14603 }, RecordDef);
14604 </code></pre>
14605  * <p>
14606  * This would consume a JSON file like this:
14607  * <pre><code>
14608 { 'results': 2, 'rows': [
14609     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14610     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14611 }
14612 </code></pre>
14613  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14614  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14615  * paged from the remote server.
14616  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14617  * @cfg {String} root name of the property which contains the Array of row objects.
14618  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14619  * @cfg {Array} fields Array of field definition objects
14620  * @constructor
14621  * Create a new JsonReader
14622  * @param {Object} meta Metadata configuration options
14623  * @param {Object} recordType Either an Array of field definition objects,
14624  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14625  */
14626 Roo.data.JsonReader = function(meta, recordType){
14627     
14628     meta = meta || {};
14629     // set some defaults:
14630     Roo.applyIf(meta, {
14631         totalProperty: 'total',
14632         successProperty : 'success',
14633         root : 'data',
14634         id : 'id'
14635     });
14636     
14637     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14638 };
14639 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14640     
14641     readerType : 'Json',
14642     
14643     /**
14644      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14645      * Used by Store query builder to append _requestMeta to params.
14646      * 
14647      */
14648     metaFromRemote : false,
14649     /**
14650      * This method is only used by a DataProxy which has retrieved data from a remote server.
14651      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14652      * @return {Object} data A data block which is used by an Roo.data.Store object as
14653      * a cache of Roo.data.Records.
14654      */
14655     read : function(response){
14656         var json = response.responseText;
14657        
14658         var o = /* eval:var:o */ eval("("+json+")");
14659         if(!o) {
14660             throw {message: "JsonReader.read: Json object not found"};
14661         }
14662         
14663         if(o.metaData){
14664             
14665             delete this.ef;
14666             this.metaFromRemote = true;
14667             this.meta = o.metaData;
14668             this.recordType = Roo.data.Record.create(o.metaData.fields);
14669             this.onMetaChange(this.meta, this.recordType, o);
14670         }
14671         return this.readRecords(o);
14672     },
14673
14674     // private function a store will implement
14675     onMetaChange : function(meta, recordType, o){
14676
14677     },
14678
14679     /**
14680          * @ignore
14681          */
14682     simpleAccess: function(obj, subsc) {
14683         return obj[subsc];
14684     },
14685
14686         /**
14687          * @ignore
14688          */
14689     getJsonAccessor: function(){
14690         var re = /[\[\.]/;
14691         return function(expr) {
14692             try {
14693                 return(re.test(expr))
14694                     ? new Function("obj", "return obj." + expr)
14695                     : function(obj){
14696                         return obj[expr];
14697                     };
14698             } catch(e){}
14699             return Roo.emptyFn;
14700         };
14701     }(),
14702
14703     /**
14704      * Create a data block containing Roo.data.Records from an XML document.
14705      * @param {Object} o An object which contains an Array of row objects in the property specified
14706      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14707      * which contains the total size of the dataset.
14708      * @return {Object} data A data block which is used by an Roo.data.Store object as
14709      * a cache of Roo.data.Records.
14710      */
14711     readRecords : function(o){
14712         /**
14713          * After any data loads, the raw JSON data is available for further custom processing.
14714          * @type Object
14715          */
14716         this.o = o;
14717         var s = this.meta, Record = this.recordType,
14718             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14719
14720 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14721         if (!this.ef) {
14722             if(s.totalProperty) {
14723                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14724                 }
14725                 if(s.successProperty) {
14726                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14727                 }
14728                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14729                 if (s.id) {
14730                         var g = this.getJsonAccessor(s.id);
14731                         this.getId = function(rec) {
14732                                 var r = g(rec);  
14733                                 return (r === undefined || r === "") ? null : r;
14734                         };
14735                 } else {
14736                         this.getId = function(){return null;};
14737                 }
14738             this.ef = [];
14739             for(var jj = 0; jj < fl; jj++){
14740                 f = fi[jj];
14741                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14742                 this.ef[jj] = this.getJsonAccessor(map);
14743             }
14744         }
14745
14746         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14747         if(s.totalProperty){
14748             var vt = parseInt(this.getTotal(o), 10);
14749             if(!isNaN(vt)){
14750                 totalRecords = vt;
14751             }
14752         }
14753         if(s.successProperty){
14754             var vs = this.getSuccess(o);
14755             if(vs === false || vs === 'false'){
14756                 success = false;
14757             }
14758         }
14759         var records = [];
14760         for(var i = 0; i < c; i++){
14761                 var n = root[i];
14762             var values = {};
14763             var id = this.getId(n);
14764             for(var j = 0; j < fl; j++){
14765                 f = fi[j];
14766             var v = this.ef[j](n);
14767             if (!f.convert) {
14768                 Roo.log('missing convert for ' + f.name);
14769                 Roo.log(f);
14770                 continue;
14771             }
14772             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14773             }
14774             var record = new Record(values, id);
14775             record.json = n;
14776             records[i] = record;
14777         }
14778         return {
14779             raw : o,
14780             success : success,
14781             records : records,
14782             totalRecords : totalRecords
14783         };
14784     },
14785     // used when loading children.. @see loadDataFromChildren
14786     toLoadData: function(rec)
14787     {
14788         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14789         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14790         return { data : data, total : data.length };
14791         
14792     }
14793 });/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804 /**
14805  * @class Roo.data.ArrayReader
14806  * @extends Roo.data.DataReader
14807  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14808  * Each element of that Array represents a row of data fields. The
14809  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14810  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14811  * <p>
14812  * Example code:.
14813  * <pre><code>
14814 var RecordDef = Roo.data.Record.create([
14815     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14816     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14817 ]);
14818 var myReader = new Roo.data.ArrayReader({
14819     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14820 }, RecordDef);
14821 </code></pre>
14822  * <p>
14823  * This would consume an Array like this:
14824  * <pre><code>
14825 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14826   </code></pre>
14827  
14828  * @constructor
14829  * Create a new JsonReader
14830  * @param {Object} meta Metadata configuration options.
14831  * @param {Object|Array} recordType Either an Array of field definition objects
14832  * 
14833  * @cfg {Array} fields Array of field definition objects
14834  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14835  * as specified to {@link Roo.data.Record#create},
14836  * or an {@link Roo.data.Record} object
14837  *
14838  * 
14839  * created using {@link Roo.data.Record#create}.
14840  */
14841 Roo.data.ArrayReader = function(meta, recordType)
14842 {    
14843     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14844 };
14845
14846 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14847     
14848       /**
14849      * Create a data block containing Roo.data.Records from an XML document.
14850      * @param {Object} o An Array of row objects which represents the dataset.
14851      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14852      * a cache of Roo.data.Records.
14853      */
14854     readRecords : function(o)
14855     {
14856         var sid = this.meta ? this.meta.id : null;
14857         var recordType = this.recordType, fields = recordType.prototype.fields;
14858         var records = [];
14859         var root = o;
14860         for(var i = 0; i < root.length; i++){
14861                 var n = root[i];
14862             var values = {};
14863             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14864             for(var j = 0, jlen = fields.length; j < jlen; j++){
14865                 var f = fields.items[j];
14866                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14867                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14868                 v = f.convert(v);
14869                 values[f.name] = v;
14870             }
14871             var record = new recordType(values, id);
14872             record.json = n;
14873             records[records.length] = record;
14874         }
14875         return {
14876             records : records,
14877             totalRecords : records.length
14878         };
14879     },
14880     // used when loading children.. @see loadDataFromChildren
14881     toLoadData: function(rec)
14882     {
14883         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14884         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14885         
14886     }
14887     
14888     
14889 });/*
14890  * - LGPL
14891  * * 
14892  */
14893
14894 /**
14895  * @class Roo.bootstrap.ComboBox
14896  * @extends Roo.bootstrap.TriggerField
14897  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14898  * @cfg {Boolean} append (true|false) default false
14899  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14900  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14901  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14902  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14903  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14904  * @cfg {Boolean} animate default true
14905  * @cfg {Boolean} emptyResultText only for touch device
14906  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14907  * @cfg {String} emptyTitle default ''
14908  * @cfg {Number} width fixed with? experimental
14909  * @constructor
14910  * Create a new ComboBox.
14911  * @param {Object} config Configuration options
14912  */
14913 Roo.bootstrap.ComboBox = function(config){
14914     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14915     this.addEvents({
14916         /**
14917          * @event expand
14918          * Fires when the dropdown list is expanded
14919         * @param {Roo.bootstrap.ComboBox} combo This combo box
14920         */
14921         'expand' : true,
14922         /**
14923          * @event collapse
14924          * Fires when the dropdown list is collapsed
14925         * @param {Roo.bootstrap.ComboBox} combo This combo box
14926         */
14927         'collapse' : true,
14928         /**
14929          * @event beforeselect
14930          * Fires before a list item is selected. Return false to cancel the selection.
14931         * @param {Roo.bootstrap.ComboBox} combo This combo box
14932         * @param {Roo.data.Record} record The data record returned from the underlying store
14933         * @param {Number} index The index of the selected item in the dropdown list
14934         */
14935         'beforeselect' : true,
14936         /**
14937          * @event select
14938          * Fires when a list item is selected
14939         * @param {Roo.bootstrap.ComboBox} combo This combo box
14940         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14941         * @param {Number} index The index of the selected item in the dropdown list
14942         */
14943         'select' : true,
14944         /**
14945          * @event beforequery
14946          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14947          * The event object passed has these properties:
14948         * @param {Roo.bootstrap.ComboBox} combo This combo box
14949         * @param {String} query The query
14950         * @param {Boolean} forceAll true to force "all" query
14951         * @param {Boolean} cancel true to cancel the query
14952         * @param {Object} e The query event object
14953         */
14954         'beforequery': true,
14955          /**
14956          * @event add
14957          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14958         * @param {Roo.bootstrap.ComboBox} combo This combo box
14959         */
14960         'add' : true,
14961         /**
14962          * @event edit
14963          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14964         * @param {Roo.bootstrap.ComboBox} combo This combo box
14965         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14966         */
14967         'edit' : true,
14968         /**
14969          * @event remove
14970          * Fires when the remove value from the combobox array
14971         * @param {Roo.bootstrap.ComboBox} combo This combo box
14972         */
14973         'remove' : true,
14974         /**
14975          * @event afterremove
14976          * Fires when the remove value from the combobox array
14977         * @param {Roo.bootstrap.ComboBox} combo This combo box
14978         */
14979         'afterremove' : true,
14980         /**
14981          * @event specialfilter
14982          * Fires when specialfilter
14983             * @param {Roo.bootstrap.ComboBox} combo This combo box
14984             */
14985         'specialfilter' : true,
14986         /**
14987          * @event tick
14988          * Fires when tick the element
14989             * @param {Roo.bootstrap.ComboBox} combo This combo box
14990             */
14991         'tick' : true,
14992         /**
14993          * @event touchviewdisplay
14994          * Fires when touch view require special display (default is using displayField)
14995             * @param {Roo.bootstrap.ComboBox} combo This combo box
14996             * @param {Object} cfg set html .
14997             */
14998         'touchviewdisplay' : true
14999         
15000     });
15001     
15002     this.item = [];
15003     this.tickItems = [];
15004     
15005     this.selectedIndex = -1;
15006     if(this.mode == 'local'){
15007         if(config.queryDelay === undefined){
15008             this.queryDelay = 10;
15009         }
15010         if(config.minChars === undefined){
15011             this.minChars = 0;
15012         }
15013     }
15014 };
15015
15016 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15017      
15018     /**
15019      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15020      * rendering into an Roo.Editor, defaults to false)
15021      */
15022     /**
15023      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15024      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15025      */
15026     /**
15027      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15028      */
15029     /**
15030      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15031      * the dropdown list (defaults to undefined, with no header element)
15032      */
15033
15034      /**
15035      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15036      */
15037      
15038      /**
15039      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15040      */
15041     listWidth: undefined,
15042     /**
15043      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15044      * mode = 'remote' or 'text' if mode = 'local')
15045      */
15046     displayField: undefined,
15047     
15048     /**
15049      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15050      * mode = 'remote' or 'value' if mode = 'local'). 
15051      * Note: use of a valueField requires the user make a selection
15052      * in order for a value to be mapped.
15053      */
15054     valueField: undefined,
15055     /**
15056      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15057      */
15058     modalTitle : '',
15059     
15060     /**
15061      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15062      * field's data value (defaults to the underlying DOM element's name)
15063      */
15064     hiddenName: undefined,
15065     /**
15066      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15067      */
15068     listClass: '',
15069     /**
15070      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15071      */
15072     selectedClass: 'active',
15073     
15074     /**
15075      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15076      */
15077     shadow:'sides',
15078     /**
15079      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15080      * anchor positions (defaults to 'tl-bl')
15081      */
15082     listAlign: 'tl-bl?',
15083     /**
15084      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15085      */
15086     maxHeight: 300,
15087     /**
15088      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15089      * query specified by the allQuery config option (defaults to 'query')
15090      */
15091     triggerAction: 'query',
15092     /**
15093      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15094      * (defaults to 4, does not apply if editable = false)
15095      */
15096     minChars : 4,
15097     /**
15098      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15099      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15100      */
15101     typeAhead: false,
15102     /**
15103      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15104      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15105      */
15106     queryDelay: 500,
15107     /**
15108      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15109      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15110      */
15111     pageSize: 0,
15112     /**
15113      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15114      * when editable = true (defaults to false)
15115      */
15116     selectOnFocus:false,
15117     /**
15118      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15119      */
15120     queryParam: 'query',
15121     /**
15122      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15123      * when mode = 'remote' (defaults to 'Loading...')
15124      */
15125     loadingText: 'Loading...',
15126     /**
15127      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15128      */
15129     resizable: false,
15130     /**
15131      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15132      */
15133     handleHeight : 8,
15134     /**
15135      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15136      * traditional select (defaults to true)
15137      */
15138     editable: true,
15139     /**
15140      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15141      */
15142     allQuery: '',
15143     /**
15144      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15145      */
15146     mode: 'remote',
15147     /**
15148      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15149      * listWidth has a higher value)
15150      */
15151     minListWidth : 70,
15152     /**
15153      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15154      * allow the user to set arbitrary text into the field (defaults to false)
15155      */
15156     forceSelection:false,
15157     /**
15158      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15159      * if typeAhead = true (defaults to 250)
15160      */
15161     typeAheadDelay : 250,
15162     /**
15163      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15164      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15165      */
15166     valueNotFoundText : undefined,
15167     /**
15168      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15169      */
15170     blockFocus : false,
15171     
15172     /**
15173      * @cfg {Boolean} disableClear Disable showing of clear button.
15174      */
15175     disableClear : false,
15176     /**
15177      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15178      */
15179     alwaysQuery : false,
15180     
15181     /**
15182      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15183      */
15184     multiple : false,
15185     
15186     /**
15187      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     invalidClass : "has-warning",
15190     
15191     /**
15192      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15193      */
15194     validClass : "has-success",
15195     
15196     /**
15197      * @cfg {Boolean} specialFilter (true|false) special filter default false
15198      */
15199     specialFilter : false,
15200     
15201     /**
15202      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15203      */
15204     mobileTouchView : true,
15205     
15206     /**
15207      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15208      */
15209     useNativeIOS : false,
15210     
15211     /**
15212      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15213      */
15214     mobile_restrict_height : false,
15215     
15216     ios_options : false,
15217     
15218     //private
15219     addicon : false,
15220     editicon: false,
15221     
15222     page: 0,
15223     hasQuery: false,
15224     append: false,
15225     loadNext: false,
15226     autoFocus : true,
15227     tickable : false,
15228     btnPosition : 'right',
15229     triggerList : true,
15230     showToggleBtn : true,
15231     animate : true,
15232     emptyResultText: 'Empty',
15233     triggerText : 'Select',
15234     emptyTitle : '',
15235     width : false,
15236     
15237     // element that contains real text value.. (when hidden is used..)
15238     
15239     getAutoCreate : function()
15240     {   
15241         var cfg = false;
15242         //render
15243         /*
15244          * Render classic select for iso
15245          */
15246         
15247         if(Roo.isIOS && this.useNativeIOS){
15248             cfg = this.getAutoCreateNativeIOS();
15249             return cfg;
15250         }
15251         
15252         /*
15253          * Touch Devices
15254          */
15255         
15256         if(Roo.isTouch && this.mobileTouchView){
15257             cfg = this.getAutoCreateTouchView();
15258             return cfg;;
15259         }
15260         
15261         /*
15262          *  Normal ComboBox
15263          */
15264         if(!this.tickable){
15265             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15266             return cfg;
15267         }
15268         
15269         /*
15270          *  ComboBox with tickable selections
15271          */
15272              
15273         var align = this.labelAlign || this.parentLabelAlign();
15274         
15275         cfg = {
15276             cls : 'form-group roo-combobox-tickable' //input-group
15277         };
15278         
15279         var btn_text_select = '';
15280         var btn_text_done = '';
15281         var btn_text_cancel = '';
15282         
15283         if (this.btn_text_show) {
15284             btn_text_select = 'Select';
15285             btn_text_done = 'Done';
15286             btn_text_cancel = 'Cancel'; 
15287         }
15288         
15289         var buttons = {
15290             tag : 'div',
15291             cls : 'tickable-buttons',
15292             cn : [
15293                 {
15294                     tag : 'button',
15295                     type : 'button',
15296                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15297                     //html : this.triggerText
15298                     html: btn_text_select
15299                 },
15300                 {
15301                     tag : 'button',
15302                     type : 'button',
15303                     name : 'ok',
15304                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15305                     //html : 'Done'
15306                     html: btn_text_done
15307                 },
15308                 {
15309                     tag : 'button',
15310                     type : 'button',
15311                     name : 'cancel',
15312                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15313                     //html : 'Cancel'
15314                     html: btn_text_cancel
15315                 }
15316             ]
15317         };
15318         
15319         if(this.editable){
15320             buttons.cn.unshift({
15321                 tag: 'input',
15322                 cls: 'roo-select2-search-field-input'
15323             });
15324         }
15325         
15326         var _this = this;
15327         
15328         Roo.each(buttons.cn, function(c){
15329             if (_this.size) {
15330                 c.cls += ' btn-' + _this.size;
15331             }
15332
15333             if (_this.disabled) {
15334                 c.disabled = true;
15335             }
15336         });
15337         
15338         var box = {
15339             tag: 'div',
15340             style : 'display: contents',
15341             cn: [
15342                 {
15343                     tag: 'input',
15344                     type : 'hidden',
15345                     cls: 'form-hidden-field'
15346                 },
15347                 {
15348                     tag: 'ul',
15349                     cls: 'roo-select2-choices',
15350                     cn:[
15351                         {
15352                             tag: 'li',
15353                             cls: 'roo-select2-search-field',
15354                             cn: [
15355                                 buttons
15356                             ]
15357                         }
15358                     ]
15359                 }
15360             ]
15361         };
15362         
15363         var combobox = {
15364             cls: 'roo-select2-container input-group roo-select2-container-multi',
15365             cn: [
15366                 
15367                 box
15368 //                {
15369 //                    tag: 'ul',
15370 //                    cls: 'typeahead typeahead-long dropdown-menu',
15371 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15372 //                }
15373             ]
15374         };
15375         
15376         if(this.hasFeedback && !this.allowBlank){
15377             
15378             var feedback = {
15379                 tag: 'span',
15380                 cls: 'glyphicon form-control-feedback'
15381             };
15382
15383             combobox.cn.push(feedback);
15384         }
15385         
15386         
15387         
15388         var indicator = {
15389             tag : 'i',
15390             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15391             tooltip : 'This field is required'
15392         };
15393         if (Roo.bootstrap.version == 4) {
15394             indicator = {
15395                 tag : 'i',
15396                 style : 'display:none'
15397             };
15398         }
15399         if (align ==='left' && this.fieldLabel.length) {
15400             
15401             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15402             
15403             cfg.cn = [
15404                 indicator,
15405                 {
15406                     tag: 'label',
15407                     'for' :  id,
15408                     cls : 'control-label col-form-label',
15409                     html : this.fieldLabel
15410
15411                 },
15412                 {
15413                     cls : "", 
15414                     cn: [
15415                         combobox
15416                     ]
15417                 }
15418
15419             ];
15420             
15421             var labelCfg = cfg.cn[1];
15422             var contentCfg = cfg.cn[2];
15423             
15424
15425             if(this.indicatorpos == 'right'){
15426                 
15427                 cfg.cn = [
15428                     {
15429                         tag: 'label',
15430                         'for' :  id,
15431                         cls : 'control-label col-form-label',
15432                         cn : [
15433                             {
15434                                 tag : 'span',
15435                                 html : this.fieldLabel
15436                             },
15437                             indicator
15438                         ]
15439                     },
15440                     {
15441                         cls : "",
15442                         cn: [
15443                             combobox
15444                         ]
15445                     }
15446
15447                 ];
15448                 
15449                 
15450                 
15451                 labelCfg = cfg.cn[0];
15452                 contentCfg = cfg.cn[1];
15453             
15454             }
15455             
15456             if(this.labelWidth > 12){
15457                 labelCfg.style = "width: " + this.labelWidth + 'px';
15458             }
15459             if(this.width * 1 > 0){
15460                 contentCfg.style = "width: " + this.width + 'px';
15461             }
15462             if(this.labelWidth < 13 && this.labelmd == 0){
15463                 this.labelmd = this.labelWidth;
15464             }
15465             
15466             if(this.labellg > 0){
15467                 labelCfg.cls += ' col-lg-' + this.labellg;
15468                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15469             }
15470             
15471             if(this.labelmd > 0){
15472                 labelCfg.cls += ' col-md-' + this.labelmd;
15473                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15474             }
15475             
15476             if(this.labelsm > 0){
15477                 labelCfg.cls += ' col-sm-' + this.labelsm;
15478                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15479             }
15480             
15481             if(this.labelxs > 0){
15482                 labelCfg.cls += ' col-xs-' + this.labelxs;
15483                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15484             }
15485                 
15486                 
15487         } else if ( this.fieldLabel.length) {
15488 //                Roo.log(" label");
15489                  cfg.cn = [
15490                    indicator,
15491                     {
15492                         tag: 'label',
15493                         //cls : 'input-group-addon',
15494                         html : this.fieldLabel
15495                     },
15496                     combobox
15497                 ];
15498                 
15499                 if(this.indicatorpos == 'right'){
15500                     cfg.cn = [
15501                         {
15502                             tag: 'label',
15503                             //cls : 'input-group-addon',
15504                             html : this.fieldLabel
15505                         },
15506                         indicator,
15507                         combobox
15508                     ];
15509                     
15510                 }
15511
15512         } else {
15513             
15514 //                Roo.log(" no label && no align");
15515                 cfg = combobox
15516                      
15517                 
15518         }
15519          
15520         var settings=this;
15521         ['xs','sm','md','lg'].map(function(size){
15522             if (settings[size]) {
15523                 cfg.cls += ' col-' + size + '-' + settings[size];
15524             }
15525         });
15526         
15527         return cfg;
15528         
15529     },
15530     
15531     _initEventsCalled : false,
15532     
15533     // private
15534     initEvents: function()
15535     {   
15536         if (this._initEventsCalled) { // as we call render... prevent looping...
15537             return;
15538         }
15539         this._initEventsCalled = true;
15540         
15541         if (!this.store) {
15542             throw "can not find store for combo";
15543         }
15544         
15545         this.indicator = this.indicatorEl();
15546         
15547         this.store = Roo.factory(this.store, Roo.data);
15548         this.store.parent = this;
15549         
15550         // if we are building from html. then this element is so complex, that we can not really
15551         // use the rendered HTML.
15552         // so we have to trash and replace the previous code.
15553         if (Roo.XComponent.build_from_html) {
15554             // remove this element....
15555             var e = this.el.dom, k=0;
15556             while (e ) { e = e.previousSibling;  ++k;}
15557
15558             this.el.remove();
15559             
15560             this.el=false;
15561             this.rendered = false;
15562             
15563             this.render(this.parent().getChildContainer(true), k);
15564         }
15565         
15566         if(Roo.isIOS && this.useNativeIOS){
15567             this.initIOSView();
15568             return;
15569         }
15570         
15571         /*
15572          * Touch Devices
15573          */
15574         
15575         if(Roo.isTouch && this.mobileTouchView){
15576             this.initTouchView();
15577             return;
15578         }
15579         
15580         if(this.tickable){
15581             this.initTickableEvents();
15582             return;
15583         }
15584         
15585         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15586         
15587         if(this.hiddenName){
15588             
15589             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15590             
15591             this.hiddenField.dom.value =
15592                 this.hiddenValue !== undefined ? this.hiddenValue :
15593                 this.value !== undefined ? this.value : '';
15594
15595             // prevent input submission
15596             this.el.dom.removeAttribute('name');
15597             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15598              
15599              
15600         }
15601         //if(Roo.isGecko){
15602         //    this.el.dom.setAttribute('autocomplete', 'off');
15603         //}
15604         
15605         var cls = 'x-combo-list';
15606         
15607         //this.list = new Roo.Layer({
15608         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15609         //});
15610         
15611         var _this = this;
15612         
15613         (function(){
15614             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15615             _this.list.setWidth(lw);
15616         }).defer(100);
15617         
15618         this.list.on('mouseover', this.onViewOver, this);
15619         this.list.on('mousemove', this.onViewMove, this);
15620         this.list.on('scroll', this.onViewScroll, this);
15621         
15622         /*
15623         this.list.swallowEvent('mousewheel');
15624         this.assetHeight = 0;
15625
15626         if(this.title){
15627             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15628             this.assetHeight += this.header.getHeight();
15629         }
15630
15631         this.innerList = this.list.createChild({cls:cls+'-inner'});
15632         this.innerList.on('mouseover', this.onViewOver, this);
15633         this.innerList.on('mousemove', this.onViewMove, this);
15634         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15635         
15636         if(this.allowBlank && !this.pageSize && !this.disableClear){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.Toolbar(this.footer);
15639            
15640         }
15641         if(this.pageSize){
15642             this.footer = this.list.createChild({cls:cls+'-ft'});
15643             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15644                     {pageSize: this.pageSize});
15645             
15646         }
15647         
15648         if (this.pageTb && this.allowBlank && !this.disableClear) {
15649             var _this = this;
15650             this.pageTb.add(new Roo.Toolbar.Fill(), {
15651                 cls: 'x-btn-icon x-btn-clear',
15652                 text: '&#160;',
15653                 handler: function()
15654                 {
15655                     _this.collapse();
15656                     _this.clearValue();
15657                     _this.onSelect(false, -1);
15658                 }
15659             });
15660         }
15661         if (this.footer) {
15662             this.assetHeight += this.footer.getHeight();
15663         }
15664         */
15665             
15666         if(!this.tpl){
15667             this.tpl = Roo.bootstrap.version == 4 ?
15668                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15669                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15670         }
15671
15672         this.view = new Roo.View(this.list, this.tpl, {
15673             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15674         });
15675         //this.view.wrapEl.setDisplayed(false);
15676         this.view.on('click', this.onViewClick, this);
15677         
15678         
15679         this.store.on('beforeload', this.onBeforeLoad, this);
15680         this.store.on('load', this.onLoad, this);
15681         this.store.on('loadexception', this.onLoadException, this);
15682         /*
15683         if(this.resizable){
15684             this.resizer = new Roo.Resizable(this.list,  {
15685                pinned:true, handles:'se'
15686             });
15687             this.resizer.on('resize', function(r, w, h){
15688                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15689                 this.listWidth = w;
15690                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15691                 this.restrictHeight();
15692             }, this);
15693             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15694         }
15695         */
15696         if(!this.editable){
15697             this.editable = true;
15698             this.setEditable(false);
15699         }
15700         
15701         /*
15702         
15703         if (typeof(this.events.add.listeners) != 'undefined') {
15704             
15705             this.addicon = this.wrap.createChild(
15706                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15707        
15708             this.addicon.on('click', function(e) {
15709                 this.fireEvent('add', this);
15710             }, this);
15711         }
15712         if (typeof(this.events.edit.listeners) != 'undefined') {
15713             
15714             this.editicon = this.wrap.createChild(
15715                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15716             if (this.addicon) {
15717                 this.editicon.setStyle('margin-left', '40px');
15718             }
15719             this.editicon.on('click', function(e) {
15720                 
15721                 // we fire even  if inothing is selected..
15722                 this.fireEvent('edit', this, this.lastData );
15723                 
15724             }, this);
15725         }
15726         */
15727         
15728         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15729             "up" : function(e){
15730                 this.inKeyMode = true;
15731                 this.selectPrev();
15732             },
15733
15734             "down" : function(e){
15735                 if(!this.isExpanded()){
15736                     this.onTriggerClick();
15737                 }else{
15738                     this.inKeyMode = true;
15739                     this.selectNext();
15740                 }
15741             },
15742
15743             "enter" : function(e){
15744 //                this.onViewClick();
15745                 //return true;
15746                 this.collapse();
15747                 
15748                 if(this.fireEvent("specialkey", this, e)){
15749                     this.onViewClick(false);
15750                 }
15751                 
15752                 return true;
15753             },
15754
15755             "esc" : function(e){
15756                 this.collapse();
15757             },
15758
15759             "tab" : function(e){
15760                 this.collapse();
15761                 
15762                 if(this.fireEvent("specialkey", this, e)){
15763                     this.onViewClick(false);
15764                 }
15765                 
15766                 return true;
15767             },
15768
15769             scope : this,
15770
15771             doRelay : function(foo, bar, hname){
15772                 if(hname == 'down' || this.scope.isExpanded()){
15773                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15774                 }
15775                 return true;
15776             },
15777
15778             forceKeyDown: true
15779         });
15780         
15781         
15782         this.queryDelay = Math.max(this.queryDelay || 10,
15783                 this.mode == 'local' ? 10 : 250);
15784         
15785         
15786         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15787         
15788         if(this.typeAhead){
15789             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15790         }
15791         if(this.editable !== false){
15792             this.inputEl().on("keyup", this.onKeyUp, this);
15793         }
15794         if(this.forceSelection){
15795             this.inputEl().on('blur', this.doForce, this);
15796         }
15797         
15798         if(this.multiple){
15799             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15800             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15801         }
15802     },
15803     
15804     initTickableEvents: function()
15805     {   
15806         this.createList();
15807         
15808         if(this.hiddenName){
15809             
15810             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15811             
15812             this.hiddenField.dom.value =
15813                 this.hiddenValue !== undefined ? this.hiddenValue :
15814                 this.value !== undefined ? this.value : '';
15815
15816             // prevent input submission
15817             this.el.dom.removeAttribute('name');
15818             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15819              
15820              
15821         }
15822         
15823 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15824         
15825         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15826         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15827         if(this.triggerList){
15828             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15829         }
15830          
15831         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15832         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15833         
15834         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15835         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15836         
15837         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15838         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15839         
15840         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15841         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15843         
15844         this.okBtn.hide();
15845         this.cancelBtn.hide();
15846         
15847         var _this = this;
15848         
15849         (function(){
15850             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15851             _this.list.setWidth(lw);
15852         }).defer(100);
15853         
15854         this.list.on('mouseover', this.onViewOver, this);
15855         this.list.on('mousemove', this.onViewMove, this);
15856         
15857         this.list.on('scroll', this.onViewScroll, this);
15858         
15859         if(!this.tpl){
15860             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15861                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15862         }
15863
15864         this.view = new Roo.View(this.list, this.tpl, {
15865             singleSelect:true,
15866             tickable:true,
15867             parent:this,
15868             store: this.store,
15869             selectedClass: this.selectedClass
15870         });
15871         
15872         //this.view.wrapEl.setDisplayed(false);
15873         this.view.on('click', this.onViewClick, this);
15874         
15875         
15876         
15877         this.store.on('beforeload', this.onBeforeLoad, this);
15878         this.store.on('load', this.onLoad, this);
15879         this.store.on('loadexception', this.onLoadException, this);
15880         
15881         if(this.editable){
15882             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15883                 "up" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectPrev();
15886                 },
15887
15888                 "down" : function(e){
15889                     this.inKeyMode = true;
15890                     this.selectNext();
15891                 },
15892
15893                 "enter" : function(e){
15894                     if(this.fireEvent("specialkey", this, e)){
15895                         this.onViewClick(false);
15896                     }
15897                     
15898                     return true;
15899                 },
15900
15901                 "esc" : function(e){
15902                     this.onTickableFooterButtonClick(e, false, false);
15903                 },
15904
15905                 "tab" : function(e){
15906                     this.fireEvent("specialkey", this, e);
15907                     
15908                     this.onTickableFooterButtonClick(e, false, false);
15909                     
15910                     return true;
15911                 },
15912
15913                 scope : this,
15914
15915                 doRelay : function(e, fn, key){
15916                     if(this.scope.isExpanded()){
15917                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15918                     }
15919                     return true;
15920                 },
15921
15922                 forceKeyDown: true
15923             });
15924         }
15925         
15926         this.queryDelay = Math.max(this.queryDelay || 10,
15927                 this.mode == 'local' ? 10 : 250);
15928         
15929         
15930         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15931         
15932         if(this.typeAhead){
15933             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15934         }
15935         
15936         if(this.editable !== false){
15937             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15938         }
15939         
15940         this.indicator = this.indicatorEl();
15941         
15942         if(this.indicator){
15943             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15944             this.indicator.hide();
15945         }
15946         
15947     },
15948
15949     onDestroy : function(){
15950         if(this.view){
15951             this.view.setStore(null);
15952             this.view.el.removeAllListeners();
15953             this.view.el.remove();
15954             this.view.purgeListeners();
15955         }
15956         if(this.list){
15957             this.list.dom.innerHTML  = '';
15958         }
15959         
15960         if(this.store){
15961             this.store.un('beforeload', this.onBeforeLoad, this);
15962             this.store.un('load', this.onLoad, this);
15963             this.store.un('loadexception', this.onLoadException, this);
15964         }
15965         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15966     },
15967
15968     // private
15969     fireKey : function(e){
15970         if(e.isNavKeyPress() && !this.list.isVisible()){
15971             this.fireEvent("specialkey", this, e);
15972         }
15973     },
15974
15975     // private
15976     onResize: function(w, h)
15977     {
15978         
15979         
15980 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15981 //        
15982 //        if(typeof w != 'number'){
15983 //            // we do not handle it!?!?
15984 //            return;
15985 //        }
15986 //        var tw = this.trigger.getWidth();
15987 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15988 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15989 //        var x = w - tw;
15990 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15991 //            
15992 //        //this.trigger.setStyle('left', x+'px');
15993 //        
15994 //        if(this.list && this.listWidth === undefined){
15995 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15996 //            this.list.setWidth(lw);
15997 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15998 //        }
15999         
16000     
16001         
16002     },
16003
16004     /**
16005      * Allow or prevent the user from directly editing the field text.  If false is passed,
16006      * the user will only be able to select from the items defined in the dropdown list.  This method
16007      * is the runtime equivalent of setting the 'editable' config option at config time.
16008      * @param {Boolean} value True to allow the user to directly edit the field text
16009      */
16010     setEditable : function(value){
16011         if(value == this.editable){
16012             return;
16013         }
16014         this.editable = value;
16015         if(!value){
16016             this.inputEl().dom.setAttribute('readOnly', true);
16017             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16018             this.inputEl().addClass('x-combo-noedit');
16019         }else{
16020             this.inputEl().dom.setAttribute('readOnly', false);
16021             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16022             this.inputEl().removeClass('x-combo-noedit');
16023         }
16024     },
16025
16026     // private
16027     
16028     onBeforeLoad : function(combo,opts){
16029         if(!this.hasFocus){
16030             return;
16031         }
16032          if (!opts.add) {
16033             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16034          }
16035         this.restrictHeight();
16036         this.selectedIndex = -1;
16037     },
16038
16039     // private
16040     onLoad : function(){
16041         
16042         this.hasQuery = false;
16043         
16044         if(!this.hasFocus){
16045             return;
16046         }
16047         
16048         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16049             this.loading.hide();
16050         }
16051         
16052         if(this.store.getCount() > 0){
16053             
16054             this.expand();
16055             this.restrictHeight();
16056             if(this.lastQuery == this.allQuery){
16057                 if(this.editable && !this.tickable){
16058                     this.inputEl().dom.select();
16059                 }
16060                 
16061                 if(
16062                     !this.selectByValue(this.value, true) &&
16063                     this.autoFocus && 
16064                     (
16065                         !this.store.lastOptions ||
16066                         typeof(this.store.lastOptions.add) == 'undefined' || 
16067                         this.store.lastOptions.add != true
16068                     )
16069                 ){
16070                     this.select(0, true);
16071                 }
16072             }else{
16073                 if(this.autoFocus){
16074                     this.selectNext();
16075                 }
16076                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16077                     this.taTask.delay(this.typeAheadDelay);
16078                 }
16079             }
16080         }else{
16081             this.onEmptyResults();
16082         }
16083         
16084         //this.el.focus();
16085     },
16086     // private
16087     onLoadException : function()
16088     {
16089         this.hasQuery = false;
16090         
16091         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16092             this.loading.hide();
16093         }
16094         
16095         if(this.tickable && this.editable){
16096             return;
16097         }
16098         
16099         this.collapse();
16100         // only causes errors at present
16101         //Roo.log(this.store.reader.jsonData);
16102         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16103             // fixme
16104             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16105         //}
16106         
16107         
16108     },
16109     // private
16110     onTypeAhead : function(){
16111         if(this.store.getCount() > 0){
16112             var r = this.store.getAt(0);
16113             var newValue = r.data[this.displayField];
16114             var len = newValue.length;
16115             var selStart = this.getRawValue().length;
16116             
16117             if(selStart != len){
16118                 this.setRawValue(newValue);
16119                 this.selectText(selStart, newValue.length);
16120             }
16121         }
16122     },
16123
16124     // private
16125     onSelect : function(record, index){
16126         
16127         if(this.fireEvent('beforeselect', this, record, index) !== false){
16128         
16129             this.setFromData(index > -1 ? record.data : false);
16130             
16131             this.collapse();
16132             this.fireEvent('select', this, record, index);
16133         }
16134     },
16135
16136     /**
16137      * Returns the currently selected field value or empty string if no value is set.
16138      * @return {String} value The selected value
16139      */
16140     getValue : function()
16141     {
16142         if(Roo.isIOS && this.useNativeIOS){
16143             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16144         }
16145         
16146         if(this.multiple){
16147             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16148         }
16149         
16150         if(this.valueField){
16151             return typeof this.value != 'undefined' ? this.value : '';
16152         }else{
16153             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16154         }
16155     },
16156     
16157     getRawValue : function()
16158     {
16159         if(Roo.isIOS && this.useNativeIOS){
16160             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16161         }
16162         
16163         var v = this.inputEl().getValue();
16164         
16165         return v;
16166     },
16167
16168     /**
16169      * Clears any text/value currently set in the field
16170      */
16171     clearValue : function(){
16172         
16173         if(this.hiddenField){
16174             this.hiddenField.dom.value = '';
16175         }
16176         this.value = '';
16177         this.setRawValue('');
16178         this.lastSelectionText = '';
16179         this.lastData = false;
16180         
16181         var close = this.closeTriggerEl();
16182         
16183         if(close){
16184             close.hide();
16185         }
16186         
16187         this.validate();
16188         
16189     },
16190
16191     /**
16192      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16193      * will be displayed in the field.  If the value does not match the data value of an existing item,
16194      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16195      * Otherwise the field will be blank (although the value will still be set).
16196      * @param {String} value The value to match
16197      */
16198     setValue : function(v)
16199     {
16200         if(Roo.isIOS && this.useNativeIOS){
16201             this.setIOSValue(v);
16202             return;
16203         }
16204         
16205         if(this.multiple){
16206             this.syncValue();
16207             return;
16208         }
16209         
16210         var text = v;
16211         if(this.valueField){
16212             var r = this.findRecord(this.valueField, v);
16213             if(r){
16214                 text = r.data[this.displayField];
16215             }else if(this.valueNotFoundText !== undefined){
16216                 text = this.valueNotFoundText;
16217             }
16218         }
16219         this.lastSelectionText = text;
16220         if(this.hiddenField){
16221             this.hiddenField.dom.value = v;
16222         }
16223         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16224         this.value = v;
16225         
16226         var close = this.closeTriggerEl();
16227         
16228         if(close){
16229             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16230         }
16231         
16232         this.validate();
16233     },
16234     /**
16235      * @property {Object} the last set data for the element
16236      */
16237     
16238     lastData : false,
16239     /**
16240      * Sets the value of the field based on a object which is related to the record format for the store.
16241      * @param {Object} value the value to set as. or false on reset?
16242      */
16243     setFromData : function(o){
16244         
16245         if(this.multiple){
16246             this.addItem(o);
16247             return;
16248         }
16249             
16250         var dv = ''; // display value
16251         var vv = ''; // value value..
16252         this.lastData = o;
16253         if (this.displayField) {
16254             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16255         } else {
16256             // this is an error condition!!!
16257             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16258         }
16259         
16260         if(this.valueField){
16261             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16262         }
16263         
16264         var close = this.closeTriggerEl();
16265         
16266         if(close){
16267             if(dv.length || vv * 1 > 0){
16268                 close.show() ;
16269                 this.blockFocus=true;
16270             } else {
16271                 close.hide();
16272             }             
16273         }
16274         
16275         if(this.hiddenField){
16276             this.hiddenField.dom.value = vv;
16277             
16278             this.lastSelectionText = dv;
16279             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16280             this.value = vv;
16281             return;
16282         }
16283         // no hidden field.. - we store the value in 'value', but still display
16284         // display field!!!!
16285         this.lastSelectionText = dv;
16286         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16287         this.value = vv;
16288         
16289         
16290         
16291     },
16292     // private
16293     reset : function(){
16294         // overridden so that last data is reset..
16295         
16296         if(this.multiple){
16297             this.clearItem();
16298             return;
16299         }
16300         
16301         this.setValue(this.originalValue);
16302         //this.clearInvalid();
16303         this.lastData = false;
16304         if (this.view) {
16305             this.view.clearSelections();
16306         }
16307         
16308         this.validate();
16309     },
16310     // private
16311     findRecord : function(prop, value){
16312         var record;
16313         if(this.store.getCount() > 0){
16314             this.store.each(function(r){
16315                 if(r.data[prop] == value){
16316                     record = r;
16317                     return false;
16318                 }
16319                 return true;
16320             });
16321         }
16322         return record;
16323     },
16324     
16325     getName: function()
16326     {
16327         // returns hidden if it's set..
16328         if (!this.rendered) {return ''};
16329         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16330         
16331     },
16332     // private
16333     onViewMove : function(e, t){
16334         this.inKeyMode = false;
16335     },
16336
16337     // private
16338     onViewOver : function(e, t){
16339         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16340             return;
16341         }
16342         var item = this.view.findItemFromChild(t);
16343         
16344         if(item){
16345             var index = this.view.indexOf(item);
16346             this.select(index, false);
16347         }
16348     },
16349
16350     // private
16351     onViewClick : function(view, doFocus, el, e)
16352     {
16353         var index = this.view.getSelectedIndexes()[0];
16354         
16355         var r = this.store.getAt(index);
16356         
16357         if(this.tickable){
16358             
16359             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16360                 return;
16361             }
16362             
16363             var rm = false;
16364             var _this = this;
16365             
16366             Roo.each(this.tickItems, function(v,k){
16367                 
16368                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16369                     Roo.log(v);
16370                     _this.tickItems.splice(k, 1);
16371                     
16372                     if(typeof(e) == 'undefined' && view == false){
16373                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16374                     }
16375                     
16376                     rm = true;
16377                     return;
16378                 }
16379             });
16380             
16381             if(rm){
16382                 return;
16383             }
16384             
16385             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16386                 this.tickItems.push(r.data);
16387             }
16388             
16389             if(typeof(e) == 'undefined' && view == false){
16390                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16391             }
16392                     
16393             return;
16394         }
16395         
16396         if(r){
16397             this.onSelect(r, index);
16398         }
16399         if(doFocus !== false && !this.blockFocus){
16400             this.inputEl().focus();
16401         }
16402     },
16403
16404     // private
16405     restrictHeight : function(){
16406         //this.innerList.dom.style.height = '';
16407         //var inner = this.innerList.dom;
16408         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16409         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16410         //this.list.beginUpdate();
16411         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16412         this.list.alignTo(this.inputEl(), this.listAlign);
16413         this.list.alignTo(this.inputEl(), this.listAlign);
16414         //this.list.endUpdate();
16415     },
16416
16417     // private
16418     onEmptyResults : function(){
16419         
16420         if(this.tickable && this.editable){
16421             this.hasFocus = false;
16422             this.restrictHeight();
16423             return;
16424         }
16425         
16426         this.collapse();
16427     },
16428
16429     /**
16430      * Returns true if the dropdown list is expanded, else false.
16431      */
16432     isExpanded : function(){
16433         return this.list.isVisible();
16434     },
16435
16436     /**
16437      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16438      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16439      * @param {String} value The data value of the item to select
16440      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16441      * selected item if it is not currently in view (defaults to true)
16442      * @return {Boolean} True if the value matched an item in the list, else false
16443      */
16444     selectByValue : function(v, scrollIntoView){
16445         if(v !== undefined && v !== null){
16446             var r = this.findRecord(this.valueField || this.displayField, v);
16447             if(r){
16448                 this.select(this.store.indexOf(r), scrollIntoView);
16449                 return true;
16450             }
16451         }
16452         return false;
16453     },
16454
16455     /**
16456      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16457      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16458      * @param {Number} index The zero-based index of the list item to select
16459      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16460      * selected item if it is not currently in view (defaults to true)
16461      */
16462     select : function(index, scrollIntoView){
16463         this.selectedIndex = index;
16464         this.view.select(index);
16465         if(scrollIntoView !== false){
16466             var el = this.view.getNode(index);
16467             /*
16468              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16469              */
16470             if(el){
16471                 this.list.scrollChildIntoView(el, false);
16472             }
16473         }
16474     },
16475
16476     // private
16477     selectNext : function(){
16478         var ct = this.store.getCount();
16479         if(ct > 0){
16480             if(this.selectedIndex == -1){
16481                 this.select(0);
16482             }else if(this.selectedIndex < ct-1){
16483                 this.select(this.selectedIndex+1);
16484             }
16485         }
16486     },
16487
16488     // private
16489     selectPrev : function(){
16490         var ct = this.store.getCount();
16491         if(ct > 0){
16492             if(this.selectedIndex == -1){
16493                 this.select(0);
16494             }else if(this.selectedIndex != 0){
16495                 this.select(this.selectedIndex-1);
16496             }
16497         }
16498     },
16499
16500     // private
16501     onKeyUp : function(e){
16502         if(this.editable !== false && !e.isSpecialKey()){
16503             this.lastKey = e.getKey();
16504             this.dqTask.delay(this.queryDelay);
16505         }
16506     },
16507
16508     // private
16509     validateBlur : function(){
16510         return !this.list || !this.list.isVisible();   
16511     },
16512
16513     // private
16514     initQuery : function(){
16515         
16516         var v = this.getRawValue();
16517         
16518         if(this.tickable && this.editable){
16519             v = this.tickableInputEl().getValue();
16520         }
16521         
16522         this.doQuery(v);
16523     },
16524
16525     // private
16526     doForce : function(){
16527         if(this.inputEl().dom.value.length > 0){
16528             this.inputEl().dom.value =
16529                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16530              
16531         }
16532     },
16533
16534     /**
16535      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16536      * query allowing the query action to be canceled if needed.
16537      * @param {String} query The SQL query to execute
16538      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16539      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16540      * saved in the current store (defaults to false)
16541      */
16542     doQuery : function(q, forceAll){
16543         
16544         if(q === undefined || q === null){
16545             q = '';
16546         }
16547         var qe = {
16548             query: q,
16549             forceAll: forceAll,
16550             combo: this,
16551             cancel:false
16552         };
16553         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16554             return false;
16555         }
16556         q = qe.query;
16557         
16558         forceAll = qe.forceAll;
16559         if(forceAll === true || (q.length >= this.minChars)){
16560             
16561             this.hasQuery = true;
16562             
16563             if(this.lastQuery != q || this.alwaysQuery){
16564                 this.lastQuery = q;
16565                 if(this.mode == 'local'){
16566                     this.selectedIndex = -1;
16567                     if(forceAll){
16568                         this.store.clearFilter();
16569                     }else{
16570                         
16571                         if(this.specialFilter){
16572                             this.fireEvent('specialfilter', this);
16573                             this.onLoad();
16574                             return;
16575                         }
16576                         
16577                         this.store.filter(this.displayField, q);
16578                     }
16579                     
16580                     this.store.fireEvent("datachanged", this.store);
16581                     
16582                     this.onLoad();
16583                     
16584                     
16585                 }else{
16586                     
16587                     this.store.baseParams[this.queryParam] = q;
16588                     
16589                     var options = {params : this.getParams(q)};
16590                     
16591                     if(this.loadNext){
16592                         options.add = true;
16593                         options.params.start = this.page * this.pageSize;
16594                     }
16595                     
16596                     this.store.load(options);
16597                     
16598                     /*
16599                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16600                      *  we should expand the list on onLoad
16601                      *  so command out it
16602                      */
16603 //                    this.expand();
16604                 }
16605             }else{
16606                 this.selectedIndex = -1;
16607                 this.onLoad();   
16608             }
16609         }
16610         
16611         this.loadNext = false;
16612     },
16613     
16614     // private
16615     getParams : function(q){
16616         var p = {};
16617         //p[this.queryParam] = q;
16618         
16619         if(this.pageSize){
16620             p.start = 0;
16621             p.limit = this.pageSize;
16622         }
16623         return p;
16624     },
16625
16626     /**
16627      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16628      */
16629     collapse : function(){
16630         if(!this.isExpanded()){
16631             return;
16632         }
16633         
16634         this.list.hide();
16635         
16636         this.hasFocus = false;
16637         
16638         if(this.tickable){
16639             this.okBtn.hide();
16640             this.cancelBtn.hide();
16641             this.trigger.show();
16642             
16643             if(this.editable){
16644                 this.tickableInputEl().dom.value = '';
16645                 this.tickableInputEl().blur();
16646             }
16647             
16648         }
16649         
16650         Roo.get(document).un('mousedown', this.collapseIf, this);
16651         Roo.get(document).un('mousewheel', this.collapseIf, this);
16652         if (!this.editable) {
16653             Roo.get(document).un('keydown', this.listKeyPress, this);
16654         }
16655         this.fireEvent('collapse', this);
16656         
16657         this.validate();
16658     },
16659
16660     // private
16661     collapseIf : function(e){
16662         var in_combo  = e.within(this.el);
16663         var in_list =  e.within(this.list);
16664         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16665         
16666         if (in_combo || in_list || is_list) {
16667             //e.stopPropagation();
16668             return;
16669         }
16670         
16671         if(this.tickable){
16672             this.onTickableFooterButtonClick(e, false, false);
16673         }
16674
16675         this.collapse();
16676         
16677     },
16678
16679     /**
16680      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16681      */
16682     expand : function(){
16683        
16684         if(this.isExpanded() || !this.hasFocus){
16685             return;
16686         }
16687         
16688         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16689         this.list.setWidth(lw);
16690         
16691         Roo.log('expand');
16692         
16693         this.list.show();
16694         
16695         this.restrictHeight();
16696         
16697         if(this.tickable){
16698             
16699             this.tickItems = Roo.apply([], this.item);
16700             
16701             this.okBtn.show();
16702             this.cancelBtn.show();
16703             this.trigger.hide();
16704             
16705             if(this.editable){
16706                 this.tickableInputEl().focus();
16707             }
16708             
16709         }
16710         
16711         Roo.get(document).on('mousedown', this.collapseIf, this);
16712         Roo.get(document).on('mousewheel', this.collapseIf, this);
16713         if (!this.editable) {
16714             Roo.get(document).on('keydown', this.listKeyPress, this);
16715         }
16716         
16717         this.fireEvent('expand', this);
16718     },
16719
16720     // private
16721     // Implements the default empty TriggerField.onTriggerClick function
16722     onTriggerClick : function(e)
16723     {
16724         Roo.log('trigger click');
16725         
16726         if(this.disabled || !this.triggerList){
16727             return;
16728         }
16729         
16730         this.page = 0;
16731         this.loadNext = false;
16732         
16733         if(this.isExpanded()){
16734             this.collapse();
16735             if (!this.blockFocus) {
16736                 this.inputEl().focus();
16737             }
16738             
16739         }else {
16740             this.hasFocus = true;
16741             if(this.triggerAction == 'all') {
16742                 this.doQuery(this.allQuery, true);
16743             } else {
16744                 this.doQuery(this.getRawValue());
16745             }
16746             if (!this.blockFocus) {
16747                 this.inputEl().focus();
16748             }
16749         }
16750     },
16751     
16752     onTickableTriggerClick : function(e)
16753     {
16754         if(this.disabled){
16755             return;
16756         }
16757         
16758         this.page = 0;
16759         this.loadNext = false;
16760         this.hasFocus = true;
16761         
16762         if(this.triggerAction == 'all') {
16763             this.doQuery(this.allQuery, true);
16764         } else {
16765             this.doQuery(this.getRawValue());
16766         }
16767     },
16768     
16769     onSearchFieldClick : function(e)
16770     {
16771         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16772             this.onTickableFooterButtonClick(e, false, false);
16773             return;
16774         }
16775         
16776         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16777             return;
16778         }
16779         
16780         this.page = 0;
16781         this.loadNext = false;
16782         this.hasFocus = true;
16783         
16784         if(this.triggerAction == 'all') {
16785             this.doQuery(this.allQuery, true);
16786         } else {
16787             this.doQuery(this.getRawValue());
16788         }
16789     },
16790     
16791     listKeyPress : function(e)
16792     {
16793         //Roo.log('listkeypress');
16794         // scroll to first matching element based on key pres..
16795         if (e.isSpecialKey()) {
16796             return false;
16797         }
16798         var k = String.fromCharCode(e.getKey()).toUpperCase();
16799         //Roo.log(k);
16800         var match  = false;
16801         var csel = this.view.getSelectedNodes();
16802         var cselitem = false;
16803         if (csel.length) {
16804             var ix = this.view.indexOf(csel[0]);
16805             cselitem  = this.store.getAt(ix);
16806             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16807                 cselitem = false;
16808             }
16809             
16810         }
16811         
16812         this.store.each(function(v) { 
16813             if (cselitem) {
16814                 // start at existing selection.
16815                 if (cselitem.id == v.id) {
16816                     cselitem = false;
16817                 }
16818                 return true;
16819             }
16820                 
16821             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16822                 match = this.store.indexOf(v);
16823                 return false;
16824             }
16825             return true;
16826         }, this);
16827         
16828         if (match === false) {
16829             return true; // no more action?
16830         }
16831         // scroll to?
16832         this.view.select(match);
16833         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16834         sn.scrollIntoView(sn.dom.parentNode, false);
16835     },
16836     
16837     onViewScroll : function(e, t){
16838         
16839         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){
16840             return;
16841         }
16842         
16843         this.hasQuery = true;
16844         
16845         this.loading = this.list.select('.loading', true).first();
16846         
16847         if(this.loading === null){
16848             this.list.createChild({
16849                 tag: 'div',
16850                 cls: 'loading roo-select2-more-results roo-select2-active',
16851                 html: 'Loading more results...'
16852             });
16853             
16854             this.loading = this.list.select('.loading', true).first();
16855             
16856             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16857             
16858             this.loading.hide();
16859         }
16860         
16861         this.loading.show();
16862         
16863         var _combo = this;
16864         
16865         this.page++;
16866         this.loadNext = true;
16867         
16868         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16869         
16870         return;
16871     },
16872     
16873     addItem : function(o)
16874     {   
16875         var dv = ''; // display value
16876         
16877         if (this.displayField) {
16878             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16879         } else {
16880             // this is an error condition!!!
16881             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16882         }
16883         
16884         if(!dv.length){
16885             return;
16886         }
16887         
16888         var choice = this.choices.createChild({
16889             tag: 'li',
16890             cls: 'roo-select2-search-choice',
16891             cn: [
16892                 {
16893                     tag: 'div',
16894                     html: dv
16895                 },
16896                 {
16897                     tag: 'a',
16898                     href: '#',
16899                     cls: 'roo-select2-search-choice-close fa fa-times',
16900                     tabindex: '-1'
16901                 }
16902             ]
16903             
16904         }, this.searchField);
16905         
16906         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16907         
16908         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16909         
16910         this.item.push(o);
16911         
16912         this.lastData = o;
16913         
16914         this.syncValue();
16915         
16916         this.inputEl().dom.value = '';
16917         
16918         this.validate();
16919     },
16920     
16921     onRemoveItem : function(e, _self, o)
16922     {
16923         e.preventDefault();
16924         
16925         this.lastItem = Roo.apply([], this.item);
16926         
16927         var index = this.item.indexOf(o.data) * 1;
16928         
16929         if( index < 0){
16930             Roo.log('not this item?!');
16931             return;
16932         }
16933         
16934         this.item.splice(index, 1);
16935         o.item.remove();
16936         
16937         this.syncValue();
16938         
16939         this.fireEvent('remove', this, e);
16940         
16941         this.validate();
16942         
16943     },
16944     
16945     syncValue : function()
16946     {
16947         if(!this.item.length){
16948             this.clearValue();
16949             return;
16950         }
16951             
16952         var value = [];
16953         var _this = this;
16954         Roo.each(this.item, function(i){
16955             if(_this.valueField){
16956                 value.push(i[_this.valueField]);
16957                 return;
16958             }
16959
16960             value.push(i);
16961         });
16962
16963         this.value = value.join(',');
16964
16965         if(this.hiddenField){
16966             this.hiddenField.dom.value = this.value;
16967         }
16968         
16969         this.store.fireEvent("datachanged", this.store);
16970         
16971         this.validate();
16972     },
16973     
16974     clearItem : function()
16975     {
16976         if(!this.multiple){
16977             return;
16978         }
16979         
16980         this.item = [];
16981         
16982         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16983            c.remove();
16984         });
16985         
16986         this.syncValue();
16987         
16988         this.validate();
16989         
16990         if(this.tickable && !Roo.isTouch){
16991             this.view.refresh();
16992         }
16993     },
16994     
16995     inputEl: function ()
16996     {
16997         if(Roo.isIOS && this.useNativeIOS){
16998             return this.el.select('select.roo-ios-select', true).first();
16999         }
17000         
17001         if(Roo.isTouch && this.mobileTouchView){
17002             return this.el.select('input.form-control',true).first();
17003         }
17004         
17005         if(this.tickable){
17006             return this.searchField;
17007         }
17008         
17009         return this.el.select('input.form-control',true).first();
17010     },
17011     
17012     onTickableFooterButtonClick : function(e, btn, el)
17013     {
17014         e.preventDefault();
17015         
17016         this.lastItem = Roo.apply([], this.item);
17017         
17018         if(btn && btn.name == 'cancel'){
17019             this.tickItems = Roo.apply([], this.item);
17020             this.collapse();
17021             return;
17022         }
17023         
17024         this.clearItem();
17025         
17026         var _this = this;
17027         
17028         Roo.each(this.tickItems, function(o){
17029             _this.addItem(o);
17030         });
17031         
17032         this.collapse();
17033         
17034     },
17035     
17036     validate : function()
17037     {
17038         if(this.getVisibilityEl().hasClass('hidden')){
17039             return true;
17040         }
17041         
17042         var v = this.getRawValue();
17043         
17044         if(this.multiple){
17045             v = this.getValue();
17046         }
17047         
17048         if(this.disabled || this.allowBlank || v.length){
17049             this.markValid();
17050             return true;
17051         }
17052         
17053         this.markInvalid();
17054         return false;
17055     },
17056     
17057     tickableInputEl : function()
17058     {
17059         if(!this.tickable || !this.editable){
17060             return this.inputEl();
17061         }
17062         
17063         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17064     },
17065     
17066     
17067     getAutoCreateTouchView : function()
17068     {
17069         var id = Roo.id();
17070         
17071         var cfg = {
17072             cls: 'form-group' //input-group
17073         };
17074         
17075         var input =  {
17076             tag: 'input',
17077             id : id,
17078             type : this.inputType,
17079             cls : 'form-control x-combo-noedit',
17080             autocomplete: 'new-password',
17081             placeholder : this.placeholder || '',
17082             readonly : true
17083         };
17084         
17085         if (this.name) {
17086             input.name = this.name;
17087         }
17088         
17089         if (this.size) {
17090             input.cls += ' input-' + this.size;
17091         }
17092         
17093         if (this.disabled) {
17094             input.disabled = true;
17095         }
17096         
17097         var inputblock = {
17098             cls : 'roo-combobox-wrap',
17099             cn : [
17100                 input
17101             ]
17102         };
17103         
17104         if(this.before){
17105             inputblock.cls += ' input-group';
17106             
17107             inputblock.cn.unshift({
17108                 tag :'span',
17109                 cls : 'input-group-addon input-group-prepend input-group-text',
17110                 html : this.before
17111             });
17112         }
17113         
17114         if(this.removable && !this.multiple){
17115             inputblock.cls += ' roo-removable';
17116             
17117             inputblock.cn.push({
17118                 tag: 'button',
17119                 html : 'x',
17120                 cls : 'roo-combo-removable-btn close'
17121             });
17122         }
17123
17124         if(this.hasFeedback && !this.allowBlank){
17125             
17126             inputblock.cls += ' has-feedback';
17127             
17128             inputblock.cn.push({
17129                 tag: 'span',
17130                 cls: 'glyphicon form-control-feedback'
17131             });
17132             
17133         }
17134         
17135         if (this.after) {
17136             
17137             inputblock.cls += (this.before) ? '' : ' input-group';
17138             
17139             inputblock.cn.push({
17140                 tag :'span',
17141                 cls : 'input-group-addon input-group-append input-group-text',
17142                 html : this.after
17143             });
17144         }
17145
17146         
17147         var ibwrap = inputblock;
17148         
17149         if(this.multiple){
17150             ibwrap = {
17151                 tag: 'ul',
17152                 cls: 'roo-select2-choices',
17153                 cn:[
17154                     {
17155                         tag: 'li',
17156                         cls: 'roo-select2-search-field',
17157                         cn: [
17158
17159                             inputblock
17160                         ]
17161                     }
17162                 ]
17163             };
17164         
17165             
17166         }
17167         
17168         var combobox = {
17169             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17170             cn: [
17171                 {
17172                     tag: 'input',
17173                     type : 'hidden',
17174                     cls: 'form-hidden-field'
17175                 },
17176                 ibwrap
17177             ]
17178         };
17179         
17180         if(!this.multiple && this.showToggleBtn){
17181             
17182             var caret = {
17183                 cls: 'caret'
17184             };
17185             
17186             if (this.caret != false) {
17187                 caret = {
17188                      tag: 'i',
17189                      cls: 'fa fa-' + this.caret
17190                 };
17191                 
17192             }
17193             
17194             combobox.cn.push({
17195                 tag :'span',
17196                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17197                 cn : [
17198                     Roo.bootstrap.version == 3 ? caret : '',
17199                     {
17200                         tag: 'span',
17201                         cls: 'combobox-clear',
17202                         cn  : [
17203                             {
17204                                 tag : 'i',
17205                                 cls: 'icon-remove'
17206                             }
17207                         ]
17208                     }
17209                 ]
17210
17211             })
17212         }
17213         
17214         if(this.multiple){
17215             combobox.cls += ' roo-select2-container-multi';
17216         }
17217         
17218         var align = this.labelAlign || this.parentLabelAlign();
17219         
17220         if (align ==='left' && this.fieldLabel.length) {
17221
17222             cfg.cn = [
17223                 {
17224                    tag : 'i',
17225                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17226                    tooltip : 'This field is required'
17227                 },
17228                 {
17229                     tag: 'label',
17230                     cls : 'control-label col-form-label',
17231                     html : this.fieldLabel
17232
17233                 },
17234                 {
17235                     cls : 'roo-combobox-wrap ', 
17236                     cn: [
17237                         combobox
17238                     ]
17239                 }
17240             ];
17241             
17242             var labelCfg = cfg.cn[1];
17243             var contentCfg = cfg.cn[2];
17244             
17245
17246             if(this.indicatorpos == 'right'){
17247                 cfg.cn = [
17248                     {
17249                         tag: 'label',
17250                         'for' :  id,
17251                         cls : 'control-label col-form-label',
17252                         cn : [
17253                             {
17254                                 tag : 'span',
17255                                 html : this.fieldLabel
17256                             },
17257                             {
17258                                 tag : 'i',
17259                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17260                                 tooltip : 'This field is required'
17261                             }
17262                         ]
17263                     },
17264                     {
17265                         cls : "roo-combobox-wrap ",
17266                         cn: [
17267                             combobox
17268                         ]
17269                     }
17270
17271                 ];
17272                 
17273                 labelCfg = cfg.cn[0];
17274                 contentCfg = cfg.cn[1];
17275             }
17276             
17277            
17278             
17279             if(this.labelWidth > 12){
17280                 labelCfg.style = "width: " + this.labelWidth + 'px';
17281             }
17282            
17283             if(this.labelWidth < 13 && this.labelmd == 0){
17284                 this.labelmd = this.labelWidth;
17285             }
17286             
17287             if(this.labellg > 0){
17288                 labelCfg.cls += ' col-lg-' + this.labellg;
17289                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17290             }
17291             
17292             if(this.labelmd > 0){
17293                 labelCfg.cls += ' col-md-' + this.labelmd;
17294                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17295             }
17296             
17297             if(this.labelsm > 0){
17298                 labelCfg.cls += ' col-sm-' + this.labelsm;
17299                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17300             }
17301             
17302             if(this.labelxs > 0){
17303                 labelCfg.cls += ' col-xs-' + this.labelxs;
17304                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17305             }
17306                 
17307                 
17308         } else if ( this.fieldLabel.length) {
17309             cfg.cn = [
17310                 {
17311                    tag : 'i',
17312                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17313                    tooltip : 'This field is required'
17314                 },
17315                 {
17316                     tag: 'label',
17317                     cls : 'control-label',
17318                     html : this.fieldLabel
17319
17320                 },
17321                 {
17322                     cls : '', 
17323                     cn: [
17324                         combobox
17325                     ]
17326                 }
17327             ];
17328             
17329             if(this.indicatorpos == 'right'){
17330                 cfg.cn = [
17331                     {
17332                         tag: 'label',
17333                         cls : 'control-label',
17334                         html : this.fieldLabel,
17335                         cn : [
17336                             {
17337                                tag : 'i',
17338                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17339                                tooltip : 'This field is required'
17340                             }
17341                         ]
17342                     },
17343                     {
17344                         cls : '', 
17345                         cn: [
17346                             combobox
17347                         ]
17348                     }
17349                 ];
17350             }
17351         } else {
17352             cfg.cn = combobox;    
17353         }
17354         
17355         
17356         var settings = this;
17357         
17358         ['xs','sm','md','lg'].map(function(size){
17359             if (settings[size]) {
17360                 cfg.cls += ' col-' + size + '-' + settings[size];
17361             }
17362         });
17363         
17364         return cfg;
17365     },
17366     
17367     initTouchView : function()
17368     {
17369         this.renderTouchView();
17370         
17371         this.touchViewEl.on('scroll', function(){
17372             this.el.dom.scrollTop = 0;
17373         }, this);
17374         
17375         this.originalValue = this.getValue();
17376         
17377         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17378         
17379         this.inputEl().on("click", this.showTouchView, this);
17380         if (this.triggerEl) {
17381             this.triggerEl.on("click", this.showTouchView, this);
17382         }
17383         
17384         
17385         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17386         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17387         
17388         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17389         
17390         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17391         this.store.on('load', this.onTouchViewLoad, this);
17392         this.store.on('loadexception', this.onTouchViewLoadException, this);
17393         
17394         if(this.hiddenName){
17395             
17396             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17397             
17398             this.hiddenField.dom.value =
17399                 this.hiddenValue !== undefined ? this.hiddenValue :
17400                 this.value !== undefined ? this.value : '';
17401         
17402             this.el.dom.removeAttribute('name');
17403             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17404         }
17405         
17406         if(this.multiple){
17407             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17408             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17409         }
17410         
17411         if(this.removable && !this.multiple){
17412             var close = this.closeTriggerEl();
17413             if(close){
17414                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17415                 close.on('click', this.removeBtnClick, this, close);
17416             }
17417         }
17418         /*
17419          * fix the bug in Safari iOS8
17420          */
17421         this.inputEl().on("focus", function(e){
17422             document.activeElement.blur();
17423         }, this);
17424         
17425         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17426         
17427         return;
17428         
17429         
17430     },
17431     
17432     renderTouchView : function()
17433     {
17434         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17435         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17438         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439         
17440         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17441         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17442         this.touchViewBodyEl.setStyle('overflow', 'auto');
17443         
17444         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17445         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17446         
17447         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17448         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17449         
17450     },
17451     
17452     showTouchView : function()
17453     {
17454         if(this.disabled){
17455             return;
17456         }
17457         
17458         this.touchViewHeaderEl.hide();
17459
17460         if(this.modalTitle.length){
17461             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17462             this.touchViewHeaderEl.show();
17463         }
17464
17465         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17466         this.touchViewEl.show();
17467
17468         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17469         
17470         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17471         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17472
17473         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17474
17475         if(this.modalTitle.length){
17476             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17477         }
17478         
17479         this.touchViewBodyEl.setHeight(bodyHeight);
17480
17481         if(this.animate){
17482             var _this = this;
17483             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17484         }else{
17485             this.touchViewEl.addClass(['in','show']);
17486         }
17487         
17488         if(this._touchViewMask){
17489             Roo.get(document.body).addClass("x-body-masked");
17490             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17491             this._touchViewMask.setStyle('z-index', 10000);
17492             this._touchViewMask.addClass('show');
17493         }
17494         
17495         this.doTouchViewQuery();
17496         
17497     },
17498     
17499     hideTouchView : function()
17500     {
17501         this.touchViewEl.removeClass(['in','show']);
17502
17503         if(this.animate){
17504             var _this = this;
17505             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17506         }else{
17507             this.touchViewEl.setStyle('display', 'none');
17508         }
17509         
17510         if(this._touchViewMask){
17511             this._touchViewMask.removeClass('show');
17512             Roo.get(document.body).removeClass("x-body-masked");
17513         }
17514     },
17515     
17516     setTouchViewValue : function()
17517     {
17518         if(this.multiple){
17519             this.clearItem();
17520         
17521             var _this = this;
17522
17523             Roo.each(this.tickItems, function(o){
17524                 this.addItem(o);
17525             }, this);
17526         }
17527         
17528         this.hideTouchView();
17529     },
17530     
17531     doTouchViewQuery : function()
17532     {
17533         var qe = {
17534             query: '',
17535             forceAll: true,
17536             combo: this,
17537             cancel:false
17538         };
17539         
17540         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17541             return false;
17542         }
17543         
17544         if(!this.alwaysQuery || this.mode == 'local'){
17545             this.onTouchViewLoad();
17546             return;
17547         }
17548         
17549         this.store.load();
17550     },
17551     
17552     onTouchViewBeforeLoad : function(combo,opts)
17553     {
17554         return;
17555     },
17556
17557     // private
17558     onTouchViewLoad : function()
17559     {
17560         if(this.store.getCount() < 1){
17561             this.onTouchViewEmptyResults();
17562             return;
17563         }
17564         
17565         this.clearTouchView();
17566         
17567         var rawValue = this.getRawValue();
17568         
17569         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17570         
17571         this.tickItems = [];
17572         
17573         this.store.data.each(function(d, rowIndex){
17574             var row = this.touchViewListGroup.createChild(template);
17575             
17576             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17577                 row.addClass(d.data.cls);
17578             }
17579             
17580             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17581                 var cfg = {
17582                     data : d.data,
17583                     html : d.data[this.displayField]
17584                 };
17585                 
17586                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17587                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17588                 }
17589             }
17590             row.removeClass('selected');
17591             if(!this.multiple && this.valueField &&
17592                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17593             {
17594                 // radio buttons..
17595                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17596                 row.addClass('selected');
17597             }
17598             
17599             if(this.multiple && this.valueField &&
17600                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17601             {
17602                 
17603                 // checkboxes...
17604                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17605                 this.tickItems.push(d.data);
17606             }
17607             
17608             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17609             
17610         }, this);
17611         
17612         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17613         
17614         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17615
17616         if(this.modalTitle.length){
17617             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17618         }
17619
17620         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17621         
17622         if(this.mobile_restrict_height && listHeight < bodyHeight){
17623             this.touchViewBodyEl.setHeight(listHeight);
17624         }
17625         
17626         var _this = this;
17627         
17628         if(firstChecked && listHeight > bodyHeight){
17629             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17630         }
17631         
17632     },
17633     
17634     onTouchViewLoadException : function()
17635     {
17636         this.hideTouchView();
17637     },
17638     
17639     onTouchViewEmptyResults : function()
17640     {
17641         this.clearTouchView();
17642         
17643         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17644         
17645         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17646         
17647     },
17648     
17649     clearTouchView : function()
17650     {
17651         this.touchViewListGroup.dom.innerHTML = '';
17652     },
17653     
17654     onTouchViewClick : function(e, el, o)
17655     {
17656         e.preventDefault();
17657         
17658         var row = o.row;
17659         var rowIndex = o.rowIndex;
17660         
17661         var r = this.store.getAt(rowIndex);
17662         
17663         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17664             
17665             if(!this.multiple){
17666                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17667                     c.dom.removeAttribute('checked');
17668                 }, this);
17669
17670                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17671
17672                 this.setFromData(r.data);
17673
17674                 var close = this.closeTriggerEl();
17675
17676                 if(close){
17677                     close.show();
17678                 }
17679
17680                 this.hideTouchView();
17681
17682                 this.fireEvent('select', this, r, rowIndex);
17683
17684                 return;
17685             }
17686
17687             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17688                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17689                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17690                 return;
17691             }
17692
17693             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694             this.addItem(r.data);
17695             this.tickItems.push(r.data);
17696         }
17697     },
17698     
17699     getAutoCreateNativeIOS : function()
17700     {
17701         var cfg = {
17702             cls: 'form-group' //input-group,
17703         };
17704         
17705         var combobox =  {
17706             tag: 'select',
17707             cls : 'roo-ios-select'
17708         };
17709         
17710         if (this.name) {
17711             combobox.name = this.name;
17712         }
17713         
17714         if (this.disabled) {
17715             combobox.disabled = true;
17716         }
17717         
17718         var settings = this;
17719         
17720         ['xs','sm','md','lg'].map(function(size){
17721             if (settings[size]) {
17722                 cfg.cls += ' col-' + size + '-' + settings[size];
17723             }
17724         });
17725         
17726         cfg.cn = combobox;
17727         
17728         return cfg;
17729         
17730     },
17731     
17732     initIOSView : function()
17733     {
17734         this.store.on('load', this.onIOSViewLoad, this);
17735         
17736         return;
17737     },
17738     
17739     onIOSViewLoad : function()
17740     {
17741         if(this.store.getCount() < 1){
17742             return;
17743         }
17744         
17745         this.clearIOSView();
17746         
17747         if(this.allowBlank) {
17748             
17749             var default_text = '-- SELECT --';
17750             
17751             if(this.placeholder.length){
17752                 default_text = this.placeholder;
17753             }
17754             
17755             if(this.emptyTitle.length){
17756                 default_text += ' - ' + this.emptyTitle + ' -';
17757             }
17758             
17759             var opt = this.inputEl().createChild({
17760                 tag: 'option',
17761                 value : 0,
17762                 html : default_text
17763             });
17764             
17765             var o = {};
17766             o[this.valueField] = 0;
17767             o[this.displayField] = default_text;
17768             
17769             this.ios_options.push({
17770                 data : o,
17771                 el : opt
17772             });
17773             
17774         }
17775         
17776         this.store.data.each(function(d, rowIndex){
17777             
17778             var html = '';
17779             
17780             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17781                 html = d.data[this.displayField];
17782             }
17783             
17784             var value = '';
17785             
17786             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17787                 value = d.data[this.valueField];
17788             }
17789             
17790             var option = {
17791                 tag: 'option',
17792                 value : value,
17793                 html : html
17794             };
17795             
17796             if(this.value == d.data[this.valueField]){
17797                 option['selected'] = true;
17798             }
17799             
17800             var opt = this.inputEl().createChild(option);
17801             
17802             this.ios_options.push({
17803                 data : d.data,
17804                 el : opt
17805             });
17806             
17807         }, this);
17808         
17809         this.inputEl().on('change', function(){
17810            this.fireEvent('select', this);
17811         }, this);
17812         
17813     },
17814     
17815     clearIOSView: function()
17816     {
17817         this.inputEl().dom.innerHTML = '';
17818         
17819         this.ios_options = [];
17820     },
17821     
17822     setIOSValue: function(v)
17823     {
17824         this.value = v;
17825         
17826         if(!this.ios_options){
17827             return;
17828         }
17829         
17830         Roo.each(this.ios_options, function(opts){
17831            
17832            opts.el.dom.removeAttribute('selected');
17833            
17834            if(opts.data[this.valueField] != v){
17835                return;
17836            }
17837            
17838            opts.el.dom.setAttribute('selected', true);
17839            
17840         }, this);
17841     }
17842
17843     /** 
17844     * @cfg {Boolean} grow 
17845     * @hide 
17846     */
17847     /** 
17848     * @cfg {Number} growMin 
17849     * @hide 
17850     */
17851     /** 
17852     * @cfg {Number} growMax 
17853     * @hide 
17854     */
17855     /**
17856      * @hide
17857      * @method autoSize
17858      */
17859 });
17860
17861 Roo.apply(Roo.bootstrap.ComboBox,  {
17862     
17863     header : {
17864         tag: 'div',
17865         cls: 'modal-header',
17866         cn: [
17867             {
17868                 tag: 'h4',
17869                 cls: 'modal-title'
17870             }
17871         ]
17872     },
17873     
17874     body : {
17875         tag: 'div',
17876         cls: 'modal-body',
17877         cn: [
17878             {
17879                 tag: 'ul',
17880                 cls: 'list-group'
17881             }
17882         ]
17883     },
17884     
17885     listItemRadio : {
17886         tag: 'li',
17887         cls: 'list-group-item',
17888         cn: [
17889             {
17890                 tag: 'span',
17891                 cls: 'roo-combobox-list-group-item-value'
17892             },
17893             {
17894                 tag: 'div',
17895                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17896                 cn: [
17897                     {
17898                         tag: 'input',
17899                         type: 'radio'
17900                     },
17901                     {
17902                         tag: 'label'
17903                     }
17904                 ]
17905             }
17906         ]
17907     },
17908     
17909     listItemCheckbox : {
17910         tag: 'li',
17911         cls: 'list-group-item',
17912         cn: [
17913             {
17914                 tag: 'span',
17915                 cls: 'roo-combobox-list-group-item-value'
17916             },
17917             {
17918                 tag: 'div',
17919                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17920                 cn: [
17921                     {
17922                         tag: 'input',
17923                         type: 'checkbox'
17924                     },
17925                     {
17926                         tag: 'label'
17927                     }
17928                 ]
17929             }
17930         ]
17931     },
17932     
17933     emptyResult : {
17934         tag: 'div',
17935         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17936     },
17937     
17938     footer : {
17939         tag: 'div',
17940         cls: 'modal-footer',
17941         cn: [
17942             {
17943                 tag: 'div',
17944                 cls: 'row',
17945                 cn: [
17946                     {
17947                         tag: 'div',
17948                         cls: 'col-xs-6 text-left',
17949                         cn: {
17950                             tag: 'button',
17951                             cls: 'btn btn-danger roo-touch-view-cancel',
17952                             html: 'Cancel'
17953                         }
17954                     },
17955                     {
17956                         tag: 'div',
17957                         cls: 'col-xs-6 text-right',
17958                         cn: {
17959                             tag: 'button',
17960                             cls: 'btn btn-success roo-touch-view-ok',
17961                             html: 'OK'
17962                         }
17963                     }
17964                 ]
17965             }
17966         ]
17967         
17968     }
17969 });
17970
17971 Roo.apply(Roo.bootstrap.ComboBox,  {
17972     
17973     touchViewTemplate : {
17974         tag: 'div',
17975         cls: 'modal fade roo-combobox-touch-view',
17976         cn: [
17977             {
17978                 tag: 'div',
17979                 cls: 'modal-dialog',
17980                 style : 'position:fixed', // we have to fix position....
17981                 cn: [
17982                     {
17983                         tag: 'div',
17984                         cls: 'modal-content',
17985                         cn: [
17986                             Roo.bootstrap.ComboBox.header,
17987                             Roo.bootstrap.ComboBox.body,
17988                             Roo.bootstrap.ComboBox.footer
17989                         ]
17990                     }
17991                 ]
17992             }
17993         ]
17994     }
17995 });/*
17996  * Based on:
17997  * Ext JS Library 1.1.1
17998  * Copyright(c) 2006-2007, Ext JS, LLC.
17999  *
18000  * Originally Released Under LGPL - original licence link has changed is not relivant.
18001  *
18002  * Fork - LGPL
18003  * <script type="text/javascript">
18004  */
18005
18006 /**
18007  * @class Roo.View
18008  * @extends Roo.util.Observable
18009  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18010  * This class also supports single and multi selection modes. <br>
18011  * Create a data model bound view:
18012  <pre><code>
18013  var store = new Roo.data.Store(...);
18014
18015  var view = new Roo.View({
18016     el : "my-element",
18017     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18018  
18019     singleSelect: true,
18020     selectedClass: "ydataview-selected",
18021     store: store
18022  });
18023
18024  // listen for node click?
18025  view.on("click", function(vw, index, node, e){
18026  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18027  });
18028
18029  // load XML data
18030  dataModel.load("foobar.xml");
18031  </code></pre>
18032  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18033  * <br><br>
18034  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18035  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18036  * 
18037  * Note: old style constructor is still suported (container, template, config)
18038  * 
18039  * @constructor
18040  * Create a new View
18041  * @param {Object} config The config object
18042  * 
18043  */
18044 Roo.View = function(config, depreciated_tpl, depreciated_config){
18045     
18046     this.parent = false;
18047     
18048     if (typeof(depreciated_tpl) == 'undefined') {
18049         // new way.. - universal constructor.
18050         Roo.apply(this, config);
18051         this.el  = Roo.get(this.el);
18052     } else {
18053         // old format..
18054         this.el  = Roo.get(config);
18055         this.tpl = depreciated_tpl;
18056         Roo.apply(this, depreciated_config);
18057     }
18058     this.wrapEl  = this.el.wrap().wrap();
18059     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18060     
18061     
18062     if(typeof(this.tpl) == "string"){
18063         this.tpl = new Roo.Template(this.tpl);
18064     } else {
18065         // support xtype ctors..
18066         this.tpl = new Roo.factory(this.tpl, Roo);
18067     }
18068     
18069     
18070     this.tpl.compile();
18071     
18072     /** @private */
18073     this.addEvents({
18074         /**
18075          * @event beforeclick
18076          * Fires before a click is processed. Returns false to cancel the default action.
18077          * @param {Roo.View} this
18078          * @param {Number} index The index of the target node
18079          * @param {HTMLElement} node The target node
18080          * @param {Roo.EventObject} e The raw event object
18081          */
18082             "beforeclick" : true,
18083         /**
18084          * @event click
18085          * Fires when a template node is clicked.
18086          * @param {Roo.View} this
18087          * @param {Number} index The index of the target node
18088          * @param {HTMLElement} node The target node
18089          * @param {Roo.EventObject} e The raw event object
18090          */
18091             "click" : true,
18092         /**
18093          * @event dblclick
18094          * Fires when a template node is double clicked.
18095          * @param {Roo.View} this
18096          * @param {Number} index The index of the target node
18097          * @param {HTMLElement} node The target node
18098          * @param {Roo.EventObject} e The raw event object
18099          */
18100             "dblclick" : true,
18101         /**
18102          * @event contextmenu
18103          * Fires when a template node is right clicked.
18104          * @param {Roo.View} this
18105          * @param {Number} index The index of the target node
18106          * @param {HTMLElement} node The target node
18107          * @param {Roo.EventObject} e The raw event object
18108          */
18109             "contextmenu" : true,
18110         /**
18111          * @event selectionchange
18112          * Fires when the selected nodes change.
18113          * @param {Roo.View} this
18114          * @param {Array} selections Array of the selected nodes
18115          */
18116             "selectionchange" : true,
18117     
18118         /**
18119          * @event beforeselect
18120          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18121          * @param {Roo.View} this
18122          * @param {HTMLElement} node The node to be selected
18123          * @param {Array} selections Array of currently selected nodes
18124          */
18125             "beforeselect" : true,
18126         /**
18127          * @event preparedata
18128          * Fires on every row to render, to allow you to change the data.
18129          * @param {Roo.View} this
18130          * @param {Object} data to be rendered (change this)
18131          */
18132           "preparedata" : true
18133           
18134           
18135         });
18136
18137
18138
18139     this.el.on({
18140         "click": this.onClick,
18141         "dblclick": this.onDblClick,
18142         "contextmenu": this.onContextMenu,
18143         scope:this
18144     });
18145
18146     this.selections = [];
18147     this.nodes = [];
18148     this.cmp = new Roo.CompositeElementLite([]);
18149     if(this.store){
18150         this.store = Roo.factory(this.store, Roo.data);
18151         this.setStore(this.store, true);
18152     }
18153     
18154     if ( this.footer && this.footer.xtype) {
18155            
18156          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18157         
18158         this.footer.dataSource = this.store;
18159         this.footer.container = fctr;
18160         this.footer = Roo.factory(this.footer, Roo);
18161         fctr.insertFirst(this.el);
18162         
18163         // this is a bit insane - as the paging toolbar seems to detach the el..
18164 //        dom.parentNode.parentNode.parentNode
18165          // they get detached?
18166     }
18167     
18168     
18169     Roo.View.superclass.constructor.call(this);
18170     
18171     
18172 };
18173
18174 Roo.extend(Roo.View, Roo.util.Observable, {
18175     
18176      /**
18177      * @cfg {Roo.data.Store} store Data store to load data from.
18178      */
18179     store : false,
18180     
18181     /**
18182      * @cfg {String|Roo.Element} el The container element.
18183      */
18184     el : '',
18185     
18186     /**
18187      * @cfg {String|Roo.Template} tpl The template used by this View 
18188      */
18189     tpl : false,
18190     /**
18191      * @cfg {String} dataName the named area of the template to use as the data area
18192      *                          Works with domtemplates roo-name="name"
18193      */
18194     dataName: false,
18195     /**
18196      * @cfg {String} selectedClass The css class to add to selected nodes
18197      */
18198     selectedClass : "x-view-selected",
18199      /**
18200      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18201      */
18202     emptyText : "",
18203     
18204     /**
18205      * @cfg {String} text to display on mask (default Loading)
18206      */
18207     mask : false,
18208     /**
18209      * @cfg {Boolean} multiSelect Allow multiple selection
18210      */
18211     multiSelect : false,
18212     /**
18213      * @cfg {Boolean} singleSelect Allow single selection
18214      */
18215     singleSelect:  false,
18216     
18217     /**
18218      * @cfg {Boolean} toggleSelect - selecting 
18219      */
18220     toggleSelect : false,
18221     
18222     /**
18223      * @cfg {Boolean} tickable - selecting 
18224      */
18225     tickable : false,
18226     
18227     /**
18228      * Returns the element this view is bound to.
18229      * @return {Roo.Element}
18230      */
18231     getEl : function(){
18232         return this.wrapEl;
18233     },
18234     
18235     
18236
18237     /**
18238      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18239      */
18240     refresh : function(){
18241         //Roo.log('refresh');
18242         var t = this.tpl;
18243         
18244         // if we are using something like 'domtemplate', then
18245         // the what gets used is:
18246         // t.applySubtemplate(NAME, data, wrapping data..)
18247         // the outer template then get' applied with
18248         //     the store 'extra data'
18249         // and the body get's added to the
18250         //      roo-name="data" node?
18251         //      <span class='roo-tpl-{name}'></span> ?????
18252         
18253         
18254         
18255         this.clearSelections();
18256         this.el.update("");
18257         var html = [];
18258         var records = this.store.getRange();
18259         if(records.length < 1) {
18260             
18261             // is this valid??  = should it render a template??
18262             
18263             this.el.update(this.emptyText);
18264             return;
18265         }
18266         var el = this.el;
18267         if (this.dataName) {
18268             this.el.update(t.apply(this.store.meta)); //????
18269             el = this.el.child('.roo-tpl-' + this.dataName);
18270         }
18271         
18272         for(var i = 0, len = records.length; i < len; i++){
18273             var data = this.prepareData(records[i].data, i, records[i]);
18274             this.fireEvent("preparedata", this, data, i, records[i]);
18275             
18276             var d = Roo.apply({}, data);
18277             
18278             if(this.tickable){
18279                 Roo.apply(d, {'roo-id' : Roo.id()});
18280                 
18281                 var _this = this;
18282             
18283                 Roo.each(this.parent.item, function(item){
18284                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18285                         return;
18286                     }
18287                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18288                 });
18289             }
18290             
18291             html[html.length] = Roo.util.Format.trim(
18292                 this.dataName ?
18293                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18294                     t.apply(d)
18295             );
18296         }
18297         
18298         
18299         
18300         el.update(html.join(""));
18301         this.nodes = el.dom.childNodes;
18302         this.updateIndexes(0);
18303     },
18304     
18305
18306     /**
18307      * Function to override to reformat the data that is sent to
18308      * the template for each node.
18309      * DEPRICATED - use the preparedata event handler.
18310      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18311      * a JSON object for an UpdateManager bound view).
18312      */
18313     prepareData : function(data, index, record)
18314     {
18315         this.fireEvent("preparedata", this, data, index, record);
18316         return data;
18317     },
18318
18319     onUpdate : function(ds, record){
18320         // Roo.log('on update');   
18321         this.clearSelections();
18322         var index = this.store.indexOf(record);
18323         var n = this.nodes[index];
18324         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18325         n.parentNode.removeChild(n);
18326         this.updateIndexes(index, index);
18327     },
18328
18329     
18330     
18331 // --------- FIXME     
18332     onAdd : function(ds, records, index)
18333     {
18334         //Roo.log(['on Add', ds, records, index] );        
18335         this.clearSelections();
18336         if(this.nodes.length == 0){
18337             this.refresh();
18338             return;
18339         }
18340         var n = this.nodes[index];
18341         for(var i = 0, len = records.length; i < len; i++){
18342             var d = this.prepareData(records[i].data, i, records[i]);
18343             if(n){
18344                 this.tpl.insertBefore(n, d);
18345             }else{
18346                 
18347                 this.tpl.append(this.el, d);
18348             }
18349         }
18350         this.updateIndexes(index);
18351     },
18352
18353     onRemove : function(ds, record, index){
18354        // Roo.log('onRemove');
18355         this.clearSelections();
18356         var el = this.dataName  ?
18357             this.el.child('.roo-tpl-' + this.dataName) :
18358             this.el; 
18359         
18360         el.dom.removeChild(this.nodes[index]);
18361         this.updateIndexes(index);
18362     },
18363
18364     /**
18365      * Refresh an individual node.
18366      * @param {Number} index
18367      */
18368     refreshNode : function(index){
18369         this.onUpdate(this.store, this.store.getAt(index));
18370     },
18371
18372     updateIndexes : function(startIndex, endIndex){
18373         var ns = this.nodes;
18374         startIndex = startIndex || 0;
18375         endIndex = endIndex || ns.length - 1;
18376         for(var i = startIndex; i <= endIndex; i++){
18377             ns[i].nodeIndex = i;
18378         }
18379     },
18380
18381     /**
18382      * Changes the data store this view uses and refresh the view.
18383      * @param {Store} store
18384      */
18385     setStore : function(store, initial){
18386         if(!initial && this.store){
18387             this.store.un("datachanged", this.refresh);
18388             this.store.un("add", this.onAdd);
18389             this.store.un("remove", this.onRemove);
18390             this.store.un("update", this.onUpdate);
18391             this.store.un("clear", this.refresh);
18392             this.store.un("beforeload", this.onBeforeLoad);
18393             this.store.un("load", this.onLoad);
18394             this.store.un("loadexception", this.onLoad);
18395         }
18396         if(store){
18397           
18398             store.on("datachanged", this.refresh, this);
18399             store.on("add", this.onAdd, this);
18400             store.on("remove", this.onRemove, this);
18401             store.on("update", this.onUpdate, this);
18402             store.on("clear", this.refresh, this);
18403             store.on("beforeload", this.onBeforeLoad, this);
18404             store.on("load", this.onLoad, this);
18405             store.on("loadexception", this.onLoad, this);
18406         }
18407         
18408         if(store){
18409             this.refresh();
18410         }
18411     },
18412     /**
18413      * onbeforeLoad - masks the loading area.
18414      *
18415      */
18416     onBeforeLoad : function(store,opts)
18417     {
18418          //Roo.log('onBeforeLoad');   
18419         if (!opts.add) {
18420             this.el.update("");
18421         }
18422         this.el.mask(this.mask ? this.mask : "Loading" ); 
18423     },
18424     onLoad : function ()
18425     {
18426         this.el.unmask();
18427     },
18428     
18429
18430     /**
18431      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18432      * @param {HTMLElement} node
18433      * @return {HTMLElement} The template node
18434      */
18435     findItemFromChild : function(node){
18436         var el = this.dataName  ?
18437             this.el.child('.roo-tpl-' + this.dataName,true) :
18438             this.el.dom; 
18439         
18440         if(!node || node.parentNode == el){
18441                     return node;
18442             }
18443             var p = node.parentNode;
18444             while(p && p != el){
18445             if(p.parentNode == el){
18446                 return p;
18447             }
18448             p = p.parentNode;
18449         }
18450             return null;
18451     },
18452
18453     /** @ignore */
18454     onClick : function(e){
18455         var item = this.findItemFromChild(e.getTarget());
18456         if(item){
18457             var index = this.indexOf(item);
18458             if(this.onItemClick(item, index, e) !== false){
18459                 this.fireEvent("click", this, index, item, e);
18460             }
18461         }else{
18462             this.clearSelections();
18463         }
18464     },
18465
18466     /** @ignore */
18467     onContextMenu : function(e){
18468         var item = this.findItemFromChild(e.getTarget());
18469         if(item){
18470             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18471         }
18472     },
18473
18474     /** @ignore */
18475     onDblClick : function(e){
18476         var item = this.findItemFromChild(e.getTarget());
18477         if(item){
18478             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18479         }
18480     },
18481
18482     onItemClick : function(item, index, e)
18483     {
18484         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18485             return false;
18486         }
18487         if (this.toggleSelect) {
18488             var m = this.isSelected(item) ? 'unselect' : 'select';
18489             //Roo.log(m);
18490             var _t = this;
18491             _t[m](item, true, false);
18492             return true;
18493         }
18494         if(this.multiSelect || this.singleSelect){
18495             if(this.multiSelect && e.shiftKey && this.lastSelection){
18496                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18497             }else{
18498                 this.select(item, this.multiSelect && e.ctrlKey);
18499                 this.lastSelection = item;
18500             }
18501             
18502             if(!this.tickable){
18503                 e.preventDefault();
18504             }
18505             
18506         }
18507         return true;
18508     },
18509
18510     /**
18511      * Get the number of selected nodes.
18512      * @return {Number}
18513      */
18514     getSelectionCount : function(){
18515         return this.selections.length;
18516     },
18517
18518     /**
18519      * Get the currently selected nodes.
18520      * @return {Array} An array of HTMLElements
18521      */
18522     getSelectedNodes : function(){
18523         return this.selections;
18524     },
18525
18526     /**
18527      * Get the indexes of the selected nodes.
18528      * @return {Array}
18529      */
18530     getSelectedIndexes : function(){
18531         var indexes = [], s = this.selections;
18532         for(var i = 0, len = s.length; i < len; i++){
18533             indexes.push(s[i].nodeIndex);
18534         }
18535         return indexes;
18536     },
18537
18538     /**
18539      * Clear all selections
18540      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18541      */
18542     clearSelections : function(suppressEvent){
18543         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18544             this.cmp.elements = this.selections;
18545             this.cmp.removeClass(this.selectedClass);
18546             this.selections = [];
18547             if(!suppressEvent){
18548                 this.fireEvent("selectionchange", this, this.selections);
18549             }
18550         }
18551     },
18552
18553     /**
18554      * Returns true if the passed node is selected
18555      * @param {HTMLElement/Number} node The node or node index
18556      * @return {Boolean}
18557      */
18558     isSelected : function(node){
18559         var s = this.selections;
18560         if(s.length < 1){
18561             return false;
18562         }
18563         node = this.getNode(node);
18564         return s.indexOf(node) !== -1;
18565     },
18566
18567     /**
18568      * Selects nodes.
18569      * @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
18570      * @param {Boolean} keepExisting (optional) true to keep existing selections
18571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18572      */
18573     select : function(nodeInfo, keepExisting, suppressEvent){
18574         if(nodeInfo instanceof Array){
18575             if(!keepExisting){
18576                 this.clearSelections(true);
18577             }
18578             for(var i = 0, len = nodeInfo.length; i < len; i++){
18579                 this.select(nodeInfo[i], true, true);
18580             }
18581             return;
18582         } 
18583         var node = this.getNode(nodeInfo);
18584         if(!node || this.isSelected(node)){
18585             return; // already selected.
18586         }
18587         if(!keepExisting){
18588             this.clearSelections(true);
18589         }
18590         
18591         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18592             Roo.fly(node).addClass(this.selectedClass);
18593             this.selections.push(node);
18594             if(!suppressEvent){
18595                 this.fireEvent("selectionchange", this, this.selections);
18596             }
18597         }
18598         
18599         
18600     },
18601       /**
18602      * Unselects nodes.
18603      * @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
18604      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18605      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18606      */
18607     unselect : function(nodeInfo, keepExisting, suppressEvent)
18608     {
18609         if(nodeInfo instanceof Array){
18610             Roo.each(this.selections, function(s) {
18611                 this.unselect(s, nodeInfo);
18612             }, this);
18613             return;
18614         }
18615         var node = this.getNode(nodeInfo);
18616         if(!node || !this.isSelected(node)){
18617             //Roo.log("not selected");
18618             return; // not selected.
18619         }
18620         // fireevent???
18621         var ns = [];
18622         Roo.each(this.selections, function(s) {
18623             if (s == node ) {
18624                 Roo.fly(node).removeClass(this.selectedClass);
18625
18626                 return;
18627             }
18628             ns.push(s);
18629         },this);
18630         
18631         this.selections= ns;
18632         this.fireEvent("selectionchange", this, this.selections);
18633     },
18634
18635     /**
18636      * Gets a template node.
18637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18638      * @return {HTMLElement} The node or null if it wasn't found
18639      */
18640     getNode : function(nodeInfo){
18641         if(typeof nodeInfo == "string"){
18642             return document.getElementById(nodeInfo);
18643         }else if(typeof nodeInfo == "number"){
18644             return this.nodes[nodeInfo];
18645         }
18646         return nodeInfo;
18647     },
18648
18649     /**
18650      * Gets a range template nodes.
18651      * @param {Number} startIndex
18652      * @param {Number} endIndex
18653      * @return {Array} An array of nodes
18654      */
18655     getNodes : function(start, end){
18656         var ns = this.nodes;
18657         start = start || 0;
18658         end = typeof end == "undefined" ? ns.length - 1 : end;
18659         var nodes = [];
18660         if(start <= end){
18661             for(var i = start; i <= end; i++){
18662                 nodes.push(ns[i]);
18663             }
18664         } else{
18665             for(var i = start; i >= end; i--){
18666                 nodes.push(ns[i]);
18667             }
18668         }
18669         return nodes;
18670     },
18671
18672     /**
18673      * Finds the index of the passed node
18674      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18675      * @return {Number} The index of the node or -1
18676      */
18677     indexOf : function(node){
18678         node = this.getNode(node);
18679         if(typeof node.nodeIndex == "number"){
18680             return node.nodeIndex;
18681         }
18682         var ns = this.nodes;
18683         for(var i = 0, len = ns.length; i < len; i++){
18684             if(ns[i] == node){
18685                 return i;
18686             }
18687         }
18688         return -1;
18689     }
18690 });
18691 /*
18692  * - LGPL
18693  *
18694  * based on jquery fullcalendar
18695  * 
18696  */
18697
18698 Roo.bootstrap = Roo.bootstrap || {};
18699 /**
18700  * @class Roo.bootstrap.Calendar
18701  * @extends Roo.bootstrap.Component
18702  * Bootstrap Calendar class
18703  * @cfg {Boolean} loadMask (true|false) default false
18704  * @cfg {Object} header generate the user specific header of the calendar, default false
18705
18706  * @constructor
18707  * Create a new Container
18708  * @param {Object} config The config object
18709  */
18710
18711
18712
18713 Roo.bootstrap.Calendar = function(config){
18714     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18715      this.addEvents({
18716         /**
18717              * @event select
18718              * Fires when a date is selected
18719              * @param {DatePicker} this
18720              * @param {Date} date The selected date
18721              */
18722         'select': true,
18723         /**
18724              * @event monthchange
18725              * Fires when the displayed month changes 
18726              * @param {DatePicker} this
18727              * @param {Date} date The selected month
18728              */
18729         'monthchange': true,
18730         /**
18731              * @event evententer
18732              * Fires when mouse over an event
18733              * @param {Calendar} this
18734              * @param {event} Event
18735              */
18736         'evententer': true,
18737         /**
18738              * @event eventleave
18739              * Fires when the mouse leaves an
18740              * @param {Calendar} this
18741              * @param {event}
18742              */
18743         'eventleave': true,
18744         /**
18745              * @event eventclick
18746              * Fires when the mouse click an
18747              * @param {Calendar} this
18748              * @param {event}
18749              */
18750         'eventclick': true
18751         
18752     });
18753
18754 };
18755
18756 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18757     
18758      /**
18759      * @cfg {Number} startDay
18760      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18761      */
18762     startDay : 0,
18763     
18764     loadMask : false,
18765     
18766     header : false,
18767       
18768     getAutoCreate : function(){
18769         
18770         
18771         var fc_button = function(name, corner, style, content ) {
18772             return Roo.apply({},{
18773                 tag : 'span',
18774                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18775                          (corner.length ?
18776                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18777                             ''
18778                         ),
18779                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18780                 unselectable: 'on'
18781             });
18782         };
18783         
18784         var header = {};
18785         
18786         if(!this.header){
18787             header = {
18788                 tag : 'table',
18789                 cls : 'fc-header',
18790                 style : 'width:100%',
18791                 cn : [
18792                     {
18793                         tag: 'tr',
18794                         cn : [
18795                             {
18796                                 tag : 'td',
18797                                 cls : 'fc-header-left',
18798                                 cn : [
18799                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18800                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18801                                     { tag: 'span', cls: 'fc-header-space' },
18802                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18803
18804
18805                                 ]
18806                             },
18807
18808                             {
18809                                 tag : 'td',
18810                                 cls : 'fc-header-center',
18811                                 cn : [
18812                                     {
18813                                         tag: 'span',
18814                                         cls: 'fc-header-title',
18815                                         cn : {
18816                                             tag: 'H2',
18817                                             html : 'month / year'
18818                                         }
18819                                     }
18820
18821                                 ]
18822                             },
18823                             {
18824                                 tag : 'td',
18825                                 cls : 'fc-header-right',
18826                                 cn : [
18827                               /*      fc_button('month', 'left', '', 'month' ),
18828                                     fc_button('week', '', '', 'week' ),
18829                                     fc_button('day', 'right', '', 'day' )
18830                                 */    
18831
18832                                 ]
18833                             }
18834
18835                         ]
18836                     }
18837                 ]
18838             };
18839         }
18840         
18841         header = this.header;
18842         
18843        
18844         var cal_heads = function() {
18845             var ret = [];
18846             // fixme - handle this.
18847             
18848             for (var i =0; i < Date.dayNames.length; i++) {
18849                 var d = Date.dayNames[i];
18850                 ret.push({
18851                     tag: 'th',
18852                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18853                     html : d.substring(0,3)
18854                 });
18855                 
18856             }
18857             ret[0].cls += ' fc-first';
18858             ret[6].cls += ' fc-last';
18859             return ret;
18860         };
18861         var cal_cell = function(n) {
18862             return  {
18863                 tag: 'td',
18864                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18865                 cn : [
18866                     {
18867                         cn : [
18868                             {
18869                                 cls: 'fc-day-number',
18870                                 html: 'D'
18871                             },
18872                             {
18873                                 cls: 'fc-day-content',
18874                              
18875                                 cn : [
18876                                      {
18877                                         style: 'position: relative;' // height: 17px;
18878                                     }
18879                                 ]
18880                             }
18881                             
18882                             
18883                         ]
18884                     }
18885                 ]
18886                 
18887             }
18888         };
18889         var cal_rows = function() {
18890             
18891             var ret = [];
18892             for (var r = 0; r < 6; r++) {
18893                 var row= {
18894                     tag : 'tr',
18895                     cls : 'fc-week',
18896                     cn : []
18897                 };
18898                 
18899                 for (var i =0; i < Date.dayNames.length; i++) {
18900                     var d = Date.dayNames[i];
18901                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18902
18903                 }
18904                 row.cn[0].cls+=' fc-first';
18905                 row.cn[0].cn[0].style = 'min-height:90px';
18906                 row.cn[6].cls+=' fc-last';
18907                 ret.push(row);
18908                 
18909             }
18910             ret[0].cls += ' fc-first';
18911             ret[4].cls += ' fc-prev-last';
18912             ret[5].cls += ' fc-last';
18913             return ret;
18914             
18915         };
18916         
18917         var cal_table = {
18918             tag: 'table',
18919             cls: 'fc-border-separate',
18920             style : 'width:100%',
18921             cellspacing  : 0,
18922             cn : [
18923                 { 
18924                     tag: 'thead',
18925                     cn : [
18926                         { 
18927                             tag: 'tr',
18928                             cls : 'fc-first fc-last',
18929                             cn : cal_heads()
18930                         }
18931                     ]
18932                 },
18933                 { 
18934                     tag: 'tbody',
18935                     cn : cal_rows()
18936                 }
18937                   
18938             ]
18939         };
18940          
18941          var cfg = {
18942             cls : 'fc fc-ltr',
18943             cn : [
18944                 header,
18945                 {
18946                     cls : 'fc-content',
18947                     style : "position: relative;",
18948                     cn : [
18949                         {
18950                             cls : 'fc-view fc-view-month fc-grid',
18951                             style : 'position: relative',
18952                             unselectable : 'on',
18953                             cn : [
18954                                 {
18955                                     cls : 'fc-event-container',
18956                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18957                                 },
18958                                 cal_table
18959                             ]
18960                         }
18961                     ]
18962     
18963                 }
18964            ] 
18965             
18966         };
18967         
18968          
18969         
18970         return cfg;
18971     },
18972     
18973     
18974     initEvents : function()
18975     {
18976         if(!this.store){
18977             throw "can not find store for calendar";
18978         }
18979         
18980         var mark = {
18981             tag: "div",
18982             cls:"x-dlg-mask",
18983             style: "text-align:center",
18984             cn: [
18985                 {
18986                     tag: "div",
18987                     style: "background-color:white;width:50%;margin:250 auto",
18988                     cn: [
18989                         {
18990                             tag: "img",
18991                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18992                         },
18993                         {
18994                             tag: "span",
18995                             html: "Loading"
18996                         }
18997                         
18998                     ]
18999                 }
19000             ]
19001         };
19002         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19003         
19004         var size = this.el.select('.fc-content', true).first().getSize();
19005         this.maskEl.setSize(size.width, size.height);
19006         this.maskEl.enableDisplayMode("block");
19007         if(!this.loadMask){
19008             this.maskEl.hide();
19009         }
19010         
19011         this.store = Roo.factory(this.store, Roo.data);
19012         this.store.on('load', this.onLoad, this);
19013         this.store.on('beforeload', this.onBeforeLoad, this);
19014         
19015         this.resize();
19016         
19017         this.cells = this.el.select('.fc-day',true);
19018         //Roo.log(this.cells);
19019         this.textNodes = this.el.query('.fc-day-number');
19020         this.cells.addClassOnOver('fc-state-hover');
19021         
19022         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19023         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19024         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19025         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19026         
19027         this.on('monthchange', this.onMonthChange, this);
19028         
19029         this.update(new Date().clearTime());
19030     },
19031     
19032     resize : function() {
19033         var sz  = this.el.getSize();
19034         
19035         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19036         this.el.select('.fc-day-content div',true).setHeight(34);
19037     },
19038     
19039     
19040     // private
19041     showPrevMonth : function(e){
19042         this.update(this.activeDate.add("mo", -1));
19043     },
19044     showToday : function(e){
19045         this.update(new Date().clearTime());
19046     },
19047     // private
19048     showNextMonth : function(e){
19049         this.update(this.activeDate.add("mo", 1));
19050     },
19051
19052     // private
19053     showPrevYear : function(){
19054         this.update(this.activeDate.add("y", -1));
19055     },
19056
19057     // private
19058     showNextYear : function(){
19059         this.update(this.activeDate.add("y", 1));
19060     },
19061
19062     
19063    // private
19064     update : function(date)
19065     {
19066         var vd = this.activeDate;
19067         this.activeDate = date;
19068 //        if(vd && this.el){
19069 //            var t = date.getTime();
19070 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19071 //                Roo.log('using add remove');
19072 //                
19073 //                this.fireEvent('monthchange', this, date);
19074 //                
19075 //                this.cells.removeClass("fc-state-highlight");
19076 //                this.cells.each(function(c){
19077 //                   if(c.dateValue == t){
19078 //                       c.addClass("fc-state-highlight");
19079 //                       setTimeout(function(){
19080 //                            try{c.dom.firstChild.focus();}catch(e){}
19081 //                       }, 50);
19082 //                       return false;
19083 //                   }
19084 //                   return true;
19085 //                });
19086 //                return;
19087 //            }
19088 //        }
19089         
19090         var days = date.getDaysInMonth();
19091         
19092         var firstOfMonth = date.getFirstDateOfMonth();
19093         var startingPos = firstOfMonth.getDay()-this.startDay;
19094         
19095         if(startingPos < this.startDay){
19096             startingPos += 7;
19097         }
19098         
19099         var pm = date.add(Date.MONTH, -1);
19100         var prevStart = pm.getDaysInMonth()-startingPos;
19101 //        
19102         this.cells = this.el.select('.fc-day',true);
19103         this.textNodes = this.el.query('.fc-day-number');
19104         this.cells.addClassOnOver('fc-state-hover');
19105         
19106         var cells = this.cells.elements;
19107         var textEls = this.textNodes;
19108         
19109         Roo.each(cells, function(cell){
19110             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19111         });
19112         
19113         days += startingPos;
19114
19115         // convert everything to numbers so it's fast
19116         var day = 86400000;
19117         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19118         //Roo.log(d);
19119         //Roo.log(pm);
19120         //Roo.log(prevStart);
19121         
19122         var today = new Date().clearTime().getTime();
19123         var sel = date.clearTime().getTime();
19124         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19125         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19126         var ddMatch = this.disabledDatesRE;
19127         var ddText = this.disabledDatesText;
19128         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19129         var ddaysText = this.disabledDaysText;
19130         var format = this.format;
19131         
19132         var setCellClass = function(cal, cell){
19133             cell.row = 0;
19134             cell.events = [];
19135             cell.more = [];
19136             //Roo.log('set Cell Class');
19137             cell.title = "";
19138             var t = d.getTime();
19139             
19140             //Roo.log(d);
19141             
19142             cell.dateValue = t;
19143             if(t == today){
19144                 cell.className += " fc-today";
19145                 cell.className += " fc-state-highlight";
19146                 cell.title = cal.todayText;
19147             }
19148             if(t == sel){
19149                 // disable highlight in other month..
19150                 //cell.className += " fc-state-highlight";
19151                 
19152             }
19153             // disabling
19154             if(t < min) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.minText;
19157                 return;
19158             }
19159             if(t > max) {
19160                 cell.className = " fc-state-disabled";
19161                 cell.title = cal.maxText;
19162                 return;
19163             }
19164             if(ddays){
19165                 if(ddays.indexOf(d.getDay()) != -1){
19166                     cell.title = ddaysText;
19167                     cell.className = " fc-state-disabled";
19168                 }
19169             }
19170             if(ddMatch && format){
19171                 var fvalue = d.dateFormat(format);
19172                 if(ddMatch.test(fvalue)){
19173                     cell.title = ddText.replace("%0", fvalue);
19174                     cell.className = " fc-state-disabled";
19175                 }
19176             }
19177             
19178             if (!cell.initialClassName) {
19179                 cell.initialClassName = cell.dom.className;
19180             }
19181             
19182             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19183         };
19184
19185         var i = 0;
19186         
19187         for(; i < startingPos; i++) {
19188             textEls[i].innerHTML = (++prevStart);
19189             d.setDate(d.getDate()+1);
19190             
19191             cells[i].className = "fc-past fc-other-month";
19192             setCellClass(this, cells[i]);
19193         }
19194         
19195         var intDay = 0;
19196         
19197         for(; i < days; i++){
19198             intDay = i - startingPos + 1;
19199             textEls[i].innerHTML = (intDay);
19200             d.setDate(d.getDate()+1);
19201             
19202             cells[i].className = ''; // "x-date-active";
19203             setCellClass(this, cells[i]);
19204         }
19205         var extraDays = 0;
19206         
19207         for(; i < 42; i++) {
19208             textEls[i].innerHTML = (++extraDays);
19209             d.setDate(d.getDate()+1);
19210             
19211             cells[i].className = "fc-future fc-other-month";
19212             setCellClass(this, cells[i]);
19213         }
19214         
19215         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19216         
19217         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19218         
19219         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19220         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19221         
19222         if(totalRows != 6){
19223             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19224             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19225         }
19226         
19227         this.fireEvent('monthchange', this, date);
19228         
19229         
19230         /*
19231         if(!this.internalRender){
19232             var main = this.el.dom.firstChild;
19233             var w = main.offsetWidth;
19234             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19235             Roo.fly(main).setWidth(w);
19236             this.internalRender = true;
19237             // opera does not respect the auto grow header center column
19238             // then, after it gets a width opera refuses to recalculate
19239             // without a second pass
19240             if(Roo.isOpera && !this.secondPass){
19241                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19242                 this.secondPass = true;
19243                 this.update.defer(10, this, [date]);
19244             }
19245         }
19246         */
19247         
19248     },
19249     
19250     findCell : function(dt) {
19251         dt = dt.clearTime().getTime();
19252         var ret = false;
19253         this.cells.each(function(c){
19254             //Roo.log("check " +c.dateValue + '?=' + dt);
19255             if(c.dateValue == dt){
19256                 ret = c;
19257                 return false;
19258             }
19259             return true;
19260         });
19261         
19262         return ret;
19263     },
19264     
19265     findCells : function(ev) {
19266         var s = ev.start.clone().clearTime().getTime();
19267        // Roo.log(s);
19268         var e= ev.end.clone().clearTime().getTime();
19269        // Roo.log(e);
19270         var ret = [];
19271         this.cells.each(function(c){
19272              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19273             
19274             if(c.dateValue > e){
19275                 return ;
19276             }
19277             if(c.dateValue < s){
19278                 return ;
19279             }
19280             ret.push(c);
19281         });
19282         
19283         return ret;    
19284     },
19285     
19286 //    findBestRow: function(cells)
19287 //    {
19288 //        var ret = 0;
19289 //        
19290 //        for (var i =0 ; i < cells.length;i++) {
19291 //            ret  = Math.max(cells[i].rows || 0,ret);
19292 //        }
19293 //        return ret;
19294 //        
19295 //    },
19296     
19297     
19298     addItem : function(ev)
19299     {
19300         // look for vertical location slot in
19301         var cells = this.findCells(ev);
19302         
19303 //        ev.row = this.findBestRow(cells);
19304         
19305         // work out the location.
19306         
19307         var crow = false;
19308         var rows = [];
19309         for(var i =0; i < cells.length; i++) {
19310             
19311             cells[i].row = cells[0].row;
19312             
19313             if(i == 0){
19314                 cells[i].row = cells[i].row + 1;
19315             }
19316             
19317             if (!crow) {
19318                 crow = {
19319                     start : cells[i],
19320                     end :  cells[i]
19321                 };
19322                 continue;
19323             }
19324             if (crow.start.getY() == cells[i].getY()) {
19325                 // on same row.
19326                 crow.end = cells[i];
19327                 continue;
19328             }
19329             // different row.
19330             rows.push(crow);
19331             crow = {
19332                 start: cells[i],
19333                 end : cells[i]
19334             };
19335             
19336         }
19337         
19338         rows.push(crow);
19339         ev.els = [];
19340         ev.rows = rows;
19341         ev.cells = cells;
19342         
19343         cells[0].events.push(ev);
19344         
19345         this.calevents.push(ev);
19346     },
19347     
19348     clearEvents: function() {
19349         
19350         if(!this.calevents){
19351             return;
19352         }
19353         
19354         Roo.each(this.cells.elements, function(c){
19355             c.row = 0;
19356             c.events = [];
19357             c.more = [];
19358         });
19359         
19360         Roo.each(this.calevents, function(e) {
19361             Roo.each(e.els, function(el) {
19362                 el.un('mouseenter' ,this.onEventEnter, this);
19363                 el.un('mouseleave' ,this.onEventLeave, this);
19364                 el.remove();
19365             },this);
19366         },this);
19367         
19368         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19369             e.remove();
19370         });
19371         
19372     },
19373     
19374     renderEvents: function()
19375     {   
19376         var _this = this;
19377         
19378         this.cells.each(function(c) {
19379             
19380             if(c.row < 5){
19381                 return;
19382             }
19383             
19384             var ev = c.events;
19385             
19386             var r = 4;
19387             if(c.row != c.events.length){
19388                 r = 4 - (4 - (c.row - c.events.length));
19389             }
19390             
19391             c.events = ev.slice(0, r);
19392             c.more = ev.slice(r);
19393             
19394             if(c.more.length && c.more.length == 1){
19395                 c.events.push(c.more.pop());
19396             }
19397             
19398             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19399             
19400         });
19401             
19402         this.cells.each(function(c) {
19403             
19404             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19405             
19406             
19407             for (var e = 0; e < c.events.length; e++){
19408                 var ev = c.events[e];
19409                 var rows = ev.rows;
19410                 
19411                 for(var i = 0; i < rows.length; i++) {
19412                 
19413                     // how many rows should it span..
19414
19415                     var  cfg = {
19416                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19417                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19418
19419                         unselectable : "on",
19420                         cn : [
19421                             {
19422                                 cls: 'fc-event-inner',
19423                                 cn : [
19424     //                                {
19425     //                                  tag:'span',
19426     //                                  cls: 'fc-event-time',
19427     //                                  html : cells.length > 1 ? '' : ev.time
19428     //                                },
19429                                     {
19430                                       tag:'span',
19431                                       cls: 'fc-event-title',
19432                                       html : String.format('{0}', ev.title)
19433                                     }
19434
19435
19436                                 ]
19437                             },
19438                             {
19439                                 cls: 'ui-resizable-handle ui-resizable-e',
19440                                 html : '&nbsp;&nbsp;&nbsp'
19441                             }
19442
19443                         ]
19444                     };
19445
19446                     if (i == 0) {
19447                         cfg.cls += ' fc-event-start';
19448                     }
19449                     if ((i+1) == rows.length) {
19450                         cfg.cls += ' fc-event-end';
19451                     }
19452
19453                     var ctr = _this.el.select('.fc-event-container',true).first();
19454                     var cg = ctr.createChild(cfg);
19455
19456                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19457                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19458
19459                     var r = (c.more.length) ? 1 : 0;
19460                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19461                     cg.setWidth(ebox.right - sbox.x -2);
19462
19463                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19464                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19465                     cg.on('click', _this.onEventClick, _this, ev);
19466
19467                     ev.els.push(cg);
19468                     
19469                 }
19470                 
19471             }
19472             
19473             
19474             if(c.more.length){
19475                 var  cfg = {
19476                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19477                     style : 'position: absolute',
19478                     unselectable : "on",
19479                     cn : [
19480                         {
19481                             cls: 'fc-event-inner',
19482                             cn : [
19483                                 {
19484                                   tag:'span',
19485                                   cls: 'fc-event-title',
19486                                   html : 'More'
19487                                 }
19488
19489
19490                             ]
19491                         },
19492                         {
19493                             cls: 'ui-resizable-handle ui-resizable-e',
19494                             html : '&nbsp;&nbsp;&nbsp'
19495                         }
19496
19497                     ]
19498                 };
19499
19500                 var ctr = _this.el.select('.fc-event-container',true).first();
19501                 var cg = ctr.createChild(cfg);
19502
19503                 var sbox = c.select('.fc-day-content',true).first().getBox();
19504                 var ebox = c.select('.fc-day-content',true).first().getBox();
19505                 //Roo.log(cg);
19506                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19507                 cg.setWidth(ebox.right - sbox.x -2);
19508
19509                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19510                 
19511             }
19512             
19513         });
19514         
19515         
19516         
19517     },
19518     
19519     onEventEnter: function (e, el,event,d) {
19520         this.fireEvent('evententer', this, el, event);
19521     },
19522     
19523     onEventLeave: function (e, el,event,d) {
19524         this.fireEvent('eventleave', this, el, event);
19525     },
19526     
19527     onEventClick: function (e, el,event,d) {
19528         this.fireEvent('eventclick', this, el, event);
19529     },
19530     
19531     onMonthChange: function () {
19532         this.store.load();
19533     },
19534     
19535     onMoreEventClick: function(e, el, more)
19536     {
19537         var _this = this;
19538         
19539         this.calpopover.placement = 'right';
19540         this.calpopover.setTitle('More');
19541         
19542         this.calpopover.setContent('');
19543         
19544         var ctr = this.calpopover.el.select('.popover-content', true).first();
19545         
19546         Roo.each(more, function(m){
19547             var cfg = {
19548                 cls : 'fc-event-hori fc-event-draggable',
19549                 html : m.title
19550             };
19551             var cg = ctr.createChild(cfg);
19552             
19553             cg.on('click', _this.onEventClick, _this, m);
19554         });
19555         
19556         this.calpopover.show(el);
19557         
19558         
19559     },
19560     
19561     onLoad: function () 
19562     {   
19563         this.calevents = [];
19564         var cal = this;
19565         
19566         if(this.store.getCount() > 0){
19567             this.store.data.each(function(d){
19568                cal.addItem({
19569                     id : d.data.id,
19570                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19571                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19572                     time : d.data.start_time,
19573                     title : d.data.title,
19574                     description : d.data.description,
19575                     venue : d.data.venue
19576                 });
19577             });
19578         }
19579         
19580         this.renderEvents();
19581         
19582         if(this.calevents.length && this.loadMask){
19583             this.maskEl.hide();
19584         }
19585     },
19586     
19587     onBeforeLoad: function()
19588     {
19589         this.clearEvents();
19590         if(this.loadMask){
19591             this.maskEl.show();
19592         }
19593     }
19594 });
19595
19596  
19597  /*
19598  * - LGPL
19599  *
19600  * element
19601  * 
19602  */
19603
19604 /**
19605  * @class Roo.bootstrap.Popover
19606  * @extends Roo.bootstrap.Component
19607  * Bootstrap Popover class
19608  * @cfg {String} html contents of the popover   (or false to use children..)
19609  * @cfg {String} title of popover (or false to hide)
19610  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19611  * @cfg {String} trigger click || hover (or false to trigger manually)
19612  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19613  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19614  *      - if false and it has a 'parent' then it will be automatically added to that element
19615  *      - if string - Roo.get  will be called 
19616  * @cfg {Number} delay - delay before showing
19617  
19618  * @constructor
19619  * Create a new Popover
19620  * @param {Object} config The config object
19621  */
19622
19623 Roo.bootstrap.Popover = function(config){
19624     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19625     
19626     this.addEvents({
19627         // raw events
19628          /**
19629          * @event show
19630          * After the popover show
19631          * 
19632          * @param {Roo.bootstrap.Popover} this
19633          */
19634         "show" : true,
19635         /**
19636          * @event hide
19637          * After the popover hide
19638          * 
19639          * @param {Roo.bootstrap.Popover} this
19640          */
19641         "hide" : true
19642     });
19643 };
19644
19645 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19646     
19647     title: false,
19648     html: false,
19649     
19650     placement : 'right',
19651     trigger : 'hover', // hover
19652     modal : false,
19653     delay : 0,
19654     
19655     over: false,
19656     
19657     can_build_overlaid : false,
19658     
19659     maskEl : false, // the mask element
19660     headerEl : false,
19661     contentEl : false,
19662     
19663     
19664     getChildContainer : function()
19665     {
19666         return this.contentEl;
19667         
19668     },
19669     getPopoverHeader : function()
19670     {
19671         this.title = true; // flag not to hide it..
19672         this.headerEl.addClass('p-0');
19673         return this.headerEl
19674     },
19675     
19676     
19677     getAutoCreate : function(){
19678          
19679         var cfg = {
19680            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19681            style: 'display:block',
19682            cn : [
19683                 {
19684                     cls : 'arrow'
19685                 },
19686                 {
19687                     cls : 'popover-inner ',
19688                     cn : [
19689                         {
19690                             tag: 'h3',
19691                             cls: 'popover-title popover-header',
19692                             html : this.title === false ? '' : this.title
19693                         },
19694                         {
19695                             cls : 'popover-content popover-body '  + (this.cls || ''),
19696                             html : this.html || ''
19697                         }
19698                     ]
19699                     
19700                 }
19701            ]
19702         };
19703         
19704         return cfg;
19705     },
19706     /**
19707      * @param {string} the title
19708      */
19709     setTitle: function(str)
19710     {
19711         this.title = str;
19712         if (this.el) {
19713             this.headerEl.dom.innerHTML = str;
19714         }
19715         
19716     },
19717     /**
19718      * @param {string} the body content
19719      */
19720     setContent: function(str)
19721     {
19722         this.html = str;
19723         if (this.contentEl) {
19724             this.contentEl.dom.innerHTML = str;
19725         }
19726         
19727     },
19728     // as it get's added to the bottom of the page.
19729     onRender : function(ct, position)
19730     {
19731         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19732         
19733         
19734         
19735         if(!this.el){
19736             var cfg = Roo.apply({},  this.getAutoCreate());
19737             cfg.id = Roo.id();
19738             
19739             if (this.cls) {
19740                 cfg.cls += ' ' + this.cls;
19741             }
19742             if (this.style) {
19743                 cfg.style = this.style;
19744             }
19745             //Roo.log("adding to ");
19746             this.el = Roo.get(document.body).createChild(cfg, position);
19747 //            Roo.log(this.el);
19748         }
19749         
19750         this.contentEl = this.el.select('.popover-content',true).first();
19751         this.headerEl =  this.el.select('.popover-title',true).first();
19752         
19753         var nitems = [];
19754         if(typeof(this.items) != 'undefined'){
19755             var items = this.items;
19756             delete this.items;
19757
19758             for(var i =0;i < items.length;i++) {
19759                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19760             }
19761         }
19762
19763         this.items = nitems;
19764         
19765         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19766         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19767         
19768         
19769         
19770         this.initEvents();
19771     },
19772     
19773     resizeMask : function()
19774     {
19775         this.maskEl.setSize(
19776             Roo.lib.Dom.getViewWidth(true),
19777             Roo.lib.Dom.getViewHeight(true)
19778         );
19779     },
19780     
19781     initEvents : function()
19782     {
19783         
19784         if (!this.modal) { 
19785             Roo.bootstrap.Popover.register(this);
19786         }
19787          
19788         this.arrowEl = this.el.select('.arrow',true).first();
19789         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19790         this.el.enableDisplayMode('block');
19791         this.el.hide();
19792  
19793         
19794         if (this.over === false && !this.parent()) {
19795             return; 
19796         }
19797         if (this.triggers === false) {
19798             return;
19799         }
19800          
19801         // support parent
19802         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19803         var triggers = this.trigger ? this.trigger.split(' ') : [];
19804         Roo.each(triggers, function(trigger) {
19805         
19806             if (trigger == 'click') {
19807                 on_el.on('click', this.toggle, this);
19808             } else if (trigger != 'manual') {
19809                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19810                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19811       
19812                 on_el.on(eventIn  ,this.enter, this);
19813                 on_el.on(eventOut, this.leave, this);
19814             }
19815         }, this);
19816     },
19817     
19818     
19819     // private
19820     timeout : null,
19821     hoverState : null,
19822     
19823     toggle : function () {
19824         this.hoverState == 'in' ? this.leave() : this.enter();
19825     },
19826     
19827     enter : function () {
19828         
19829         clearTimeout(this.timeout);
19830     
19831         this.hoverState = 'in';
19832     
19833         if (!this.delay || !this.delay.show) {
19834             this.show();
19835             return;
19836         }
19837         var _t = this;
19838         this.timeout = setTimeout(function () {
19839             if (_t.hoverState == 'in') {
19840                 _t.show();
19841             }
19842         }, this.delay.show)
19843     },
19844     
19845     leave : function() {
19846         clearTimeout(this.timeout);
19847     
19848         this.hoverState = 'out';
19849     
19850         if (!this.delay || !this.delay.hide) {
19851             this.hide();
19852             return;
19853         }
19854         var _t = this;
19855         this.timeout = setTimeout(function () {
19856             if (_t.hoverState == 'out') {
19857                 _t.hide();
19858             }
19859         }, this.delay.hide)
19860     },
19861     /**
19862      * Show the popover
19863      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19864      * @param {string} (left|right|top|bottom) position
19865      */
19866     show : function (on_el)
19867     {
19868         
19869         on_el = on_el || false; // default to false
19870         var align = on_el && typeof(on_el._align) != 'undefined' ? on_el._align : false;
19871         
19872         if (!on_el) {
19873             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19874                 on_el = this.parent().el;
19875             } else if (this.over) {
19876                 Roo.get(this.over);
19877             }
19878             
19879         }
19880         
19881         if (!this.el) {
19882             this.render(document.body);
19883         }
19884         
19885         
19886         this.el.removeClass([
19887             'fade','top','bottom', 'left', 'right','in',
19888             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19889         ]);
19890         
19891         if (this.title === false) {
19892             this.headerEl.hide();
19893         }
19894         
19895         
19896         var placement = typeof this.placement == 'function' ?
19897             this.placement.call(this, this.el, on_el) :
19898             this.placement;
19899             
19900         /*
19901         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19902         
19903         // I think  'auto right' - but 
19904         
19905         var autoPlace = autoToken.test(placement);
19906         if (autoPlace) {
19907             placement = placement.replace(autoToken, '') || 'top';
19908         }
19909         */
19910         
19911         
19912         this.el.show();
19913         this.el.dom.style.display='block';
19914         
19915         //this.el.appendTo(on_el);
19916         
19917         var p = this.getPosition();
19918         var box = this.el.getBox();
19919         
19920         
19921         this.alignment = align || Roo.bootstrap.Popover.alignment[placement];
19922         this.el.addClass(this.alignment[2]);
19923
19924 //        Roo.log(align);
19925
19926         if (on_el) {
19927             this.alignEl = on_el;
19928             this.updatePosition();
19929              
19930         } else {
19931             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19932             var es = this.el.getSize();
19933             var x = Roo.lib.Dom.getViewWidth()/2;
19934             var y = Roo.lib.Dom.getViewHeight()/2;
19935             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19936             
19937         }
19938
19939         
19940         //var arrow = this.el.select('.arrow',true).first();
19941         //arrow.set(align[2], 
19942         
19943         this.el.addClass('in');
19944         
19945          
19946         
19947         this.hoverState = 'in';
19948         
19949         if (this.modal) {
19950             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19951             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19952             this.maskEl.dom.style.display = 'block';
19953             this.maskEl.addClass('show');
19954         }
19955         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19956  
19957         this.fireEvent('show', this);
19958         
19959     },
19960     /**
19961      * fire this manually after loading a grid in the table for example
19962      *
19963      */
19964     updatePosition : function()
19965     {
19966         if (!this.alignEl || !this.alignment) {
19967             return;
19968         }
19969         this.el.alignTo(this.alignEl , this.alignment[0],this.alignment[1]);
19970         
19971         // work out the pointy position.
19972         var p1 = this.alignment[0].split('-').pop().replace('?','');
19973         var xy = this.alignEl.getAnchorXY(p1, false);
19974         xy[0]+=2;xy[1]+=5;
19975         this.arrowEl.setXY(xy);
19976         
19977     },
19978     
19979     hide : function()
19980     {
19981         this.el.setXY([0,0]);
19982         this.el.removeClass('in');
19983         this.el.hide();
19984         this.hoverState = null;
19985         this.maskEl.hide(); // always..
19986         this.fireEvent('hide', this);
19987     }
19988     
19989 });
19990
19991
19992 Roo.apply(Roo.bootstrap.Popover, {
19993
19994     alignment : {
19995         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19996         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19997         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19998         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19999     },
20000     
20001     zIndex : 20001,
20002
20003     clickHander : false,
20004     
20005
20006     onMouseDown : function(e)
20007     {
20008         if (!e.getTarget(".roo-popover")) {
20009             this.hideAll();
20010         }
20011          
20012     },
20013     
20014     popups : [],
20015     
20016     register : function(popup)
20017     {
20018         if (!Roo.bootstrap.Popover.clickHandler) {
20019             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20020         }
20021         // hide other popups.
20022         this.hideAll();
20023         this.popups.push(popup);
20024     },
20025     hideAll : function()
20026     {
20027         this.popups.forEach(function(p) {
20028             p.hide();
20029         });
20030     }
20031
20032 });/*
20033  * - LGPL
20034  *
20035  * Card header - holder for the card header elements.
20036  * 
20037  */
20038
20039 /**
20040  * @class Roo.bootstrap.PopoverNav
20041  * @extends Roo.bootstrap.NavGroup
20042  * Bootstrap Popover header navigation class
20043  * @constructor
20044  * Create a new Popover Header Navigation 
20045  * @param {Object} config The config object
20046  */
20047
20048 Roo.bootstrap.PopoverNav = function(config){
20049     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20050 };
20051
20052 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20053     
20054     
20055     container_method : 'getPopoverHeader' 
20056     
20057      
20058     
20059     
20060    
20061 });
20062
20063  
20064
20065  /*
20066  * - LGPL
20067  *
20068  * Progress
20069  * 
20070  */
20071
20072 /**
20073  * @class Roo.bootstrap.Progress
20074  * @extends Roo.bootstrap.Component
20075  * Bootstrap Progress class
20076  * @cfg {Boolean} striped striped of the progress bar
20077  * @cfg {Boolean} active animated of the progress bar
20078  * 
20079  * 
20080  * @constructor
20081  * Create a new Progress
20082  * @param {Object} config The config object
20083  */
20084
20085 Roo.bootstrap.Progress = function(config){
20086     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20087 };
20088
20089 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20090     
20091     striped : false,
20092     active: false,
20093     
20094     getAutoCreate : function(){
20095         var cfg = {
20096             tag: 'div',
20097             cls: 'progress'
20098         };
20099         
20100         
20101         if(this.striped){
20102             cfg.cls += ' progress-striped';
20103         }
20104       
20105         if(this.active){
20106             cfg.cls += ' active';
20107         }
20108         
20109         
20110         return cfg;
20111     }
20112    
20113 });
20114
20115  
20116
20117  /*
20118  * - LGPL
20119  *
20120  * ProgressBar
20121  * 
20122  */
20123
20124 /**
20125  * @class Roo.bootstrap.ProgressBar
20126  * @extends Roo.bootstrap.Component
20127  * Bootstrap ProgressBar class
20128  * @cfg {Number} aria_valuenow aria-value now
20129  * @cfg {Number} aria_valuemin aria-value min
20130  * @cfg {Number} aria_valuemax aria-value max
20131  * @cfg {String} label label for the progress bar
20132  * @cfg {String} panel (success | info | warning | danger )
20133  * @cfg {String} role role of the progress bar
20134  * @cfg {String} sr_only text
20135  * 
20136  * 
20137  * @constructor
20138  * Create a new ProgressBar
20139  * @param {Object} config The config object
20140  */
20141
20142 Roo.bootstrap.ProgressBar = function(config){
20143     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20144 };
20145
20146 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20147     
20148     aria_valuenow : 0,
20149     aria_valuemin : 0,
20150     aria_valuemax : 100,
20151     label : false,
20152     panel : false,
20153     role : false,
20154     sr_only: false,
20155     
20156     getAutoCreate : function()
20157     {
20158         
20159         var cfg = {
20160             tag: 'div',
20161             cls: 'progress-bar',
20162             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20163         };
20164         
20165         if(this.sr_only){
20166             cfg.cn = {
20167                 tag: 'span',
20168                 cls: 'sr-only',
20169                 html: this.sr_only
20170             }
20171         }
20172         
20173         if(this.role){
20174             cfg.role = this.role;
20175         }
20176         
20177         if(this.aria_valuenow){
20178             cfg['aria-valuenow'] = this.aria_valuenow;
20179         }
20180         
20181         if(this.aria_valuemin){
20182             cfg['aria-valuemin'] = this.aria_valuemin;
20183         }
20184         
20185         if(this.aria_valuemax){
20186             cfg['aria-valuemax'] = this.aria_valuemax;
20187         }
20188         
20189         if(this.label && !this.sr_only){
20190             cfg.html = this.label;
20191         }
20192         
20193         if(this.panel){
20194             cfg.cls += ' progress-bar-' + this.panel;
20195         }
20196         
20197         return cfg;
20198     },
20199     
20200     update : function(aria_valuenow)
20201     {
20202         this.aria_valuenow = aria_valuenow;
20203         
20204         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20205     }
20206    
20207 });
20208
20209  
20210
20211  /*
20212  * - LGPL
20213  *
20214  * column
20215  * 
20216  */
20217
20218 /**
20219  * @class Roo.bootstrap.TabGroup
20220  * @extends Roo.bootstrap.Column
20221  * Bootstrap Column class
20222  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20223  * @cfg {Boolean} carousel true to make the group behave like a carousel
20224  * @cfg {Boolean} bullets show bullets for the panels
20225  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20226  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20227  * @cfg {Boolean} showarrow (true|false) show arrow default true
20228  * 
20229  * @constructor
20230  * Create a new TabGroup
20231  * @param {Object} config The config object
20232  */
20233
20234 Roo.bootstrap.TabGroup = function(config){
20235     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20236     if (!this.navId) {
20237         this.navId = Roo.id();
20238     }
20239     this.tabs = [];
20240     Roo.bootstrap.TabGroup.register(this);
20241     
20242 };
20243
20244 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20245     
20246     carousel : false,
20247     transition : false,
20248     bullets : 0,
20249     timer : 0,
20250     autoslide : false,
20251     slideFn : false,
20252     slideOnTouch : false,
20253     showarrow : true,
20254     
20255     getAutoCreate : function()
20256     {
20257         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20258         
20259         cfg.cls += ' tab-content';
20260         
20261         if (this.carousel) {
20262             cfg.cls += ' carousel slide';
20263             
20264             cfg.cn = [{
20265                cls : 'carousel-inner',
20266                cn : []
20267             }];
20268         
20269             if(this.bullets  && !Roo.isTouch){
20270                 
20271                 var bullets = {
20272                     cls : 'carousel-bullets',
20273                     cn : []
20274                 };
20275                
20276                 if(this.bullets_cls){
20277                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20278                 }
20279                 
20280                 bullets.cn.push({
20281                     cls : 'clear'
20282                 });
20283                 
20284                 cfg.cn[0].cn.push(bullets);
20285             }
20286             
20287             if(this.showarrow){
20288                 cfg.cn[0].cn.push({
20289                     tag : 'div',
20290                     class : 'carousel-arrow',
20291                     cn : [
20292                         {
20293                             tag : 'div',
20294                             class : 'carousel-prev',
20295                             cn : [
20296                                 {
20297                                     tag : 'i',
20298                                     class : 'fa fa-chevron-left'
20299                                 }
20300                             ]
20301                         },
20302                         {
20303                             tag : 'div',
20304                             class : 'carousel-next',
20305                             cn : [
20306                                 {
20307                                     tag : 'i',
20308                                     class : 'fa fa-chevron-right'
20309                                 }
20310                             ]
20311                         }
20312                     ]
20313                 });
20314             }
20315             
20316         }
20317         
20318         return cfg;
20319     },
20320     
20321     initEvents:  function()
20322     {
20323 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20324 //            this.el.on("touchstart", this.onTouchStart, this);
20325 //        }
20326         
20327         if(this.autoslide){
20328             var _this = this;
20329             
20330             this.slideFn = window.setInterval(function() {
20331                 _this.showPanelNext();
20332             }, this.timer);
20333         }
20334         
20335         if(this.showarrow){
20336             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20337             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20338         }
20339         
20340         
20341     },
20342     
20343 //    onTouchStart : function(e, el, o)
20344 //    {
20345 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20346 //            return;
20347 //        }
20348 //        
20349 //        this.showPanelNext();
20350 //    },
20351     
20352     
20353     getChildContainer : function()
20354     {
20355         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20356     },
20357     
20358     /**
20359     * register a Navigation item
20360     * @param {Roo.bootstrap.NavItem} the navitem to add
20361     */
20362     register : function(item)
20363     {
20364         this.tabs.push( item);
20365         item.navId = this.navId; // not really needed..
20366         this.addBullet();
20367     
20368     },
20369     
20370     getActivePanel : function()
20371     {
20372         var r = false;
20373         Roo.each(this.tabs, function(t) {
20374             if (t.active) {
20375                 r = t;
20376                 return false;
20377             }
20378             return null;
20379         });
20380         return r;
20381         
20382     },
20383     getPanelByName : function(n)
20384     {
20385         var r = false;
20386         Roo.each(this.tabs, function(t) {
20387             if (t.tabId == n) {
20388                 r = t;
20389                 return false;
20390             }
20391             return null;
20392         });
20393         return r;
20394     },
20395     indexOfPanel : function(p)
20396     {
20397         var r = false;
20398         Roo.each(this.tabs, function(t,i) {
20399             if (t.tabId == p.tabId) {
20400                 r = i;
20401                 return false;
20402             }
20403             return null;
20404         });
20405         return r;
20406     },
20407     /**
20408      * show a specific panel
20409      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20410      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20411      */
20412     showPanel : function (pan)
20413     {
20414         if(this.transition || typeof(pan) == 'undefined'){
20415             Roo.log("waiting for the transitionend");
20416             return false;
20417         }
20418         
20419         if (typeof(pan) == 'number') {
20420             pan = this.tabs[pan];
20421         }
20422         
20423         if (typeof(pan) == 'string') {
20424             pan = this.getPanelByName(pan);
20425         }
20426         
20427         var cur = this.getActivePanel();
20428         
20429         if(!pan || !cur){
20430             Roo.log('pan or acitve pan is undefined');
20431             return false;
20432         }
20433         
20434         if (pan.tabId == this.getActivePanel().tabId) {
20435             return true;
20436         }
20437         
20438         if (false === cur.fireEvent('beforedeactivate')) {
20439             return false;
20440         }
20441         
20442         if(this.bullets > 0 && !Roo.isTouch){
20443             this.setActiveBullet(this.indexOfPanel(pan));
20444         }
20445         
20446         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20447             
20448             //class="carousel-item carousel-item-next carousel-item-left"
20449             
20450             this.transition = true;
20451             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20452             var lr = dir == 'next' ? 'left' : 'right';
20453             pan.el.addClass(dir); // or prev
20454             pan.el.addClass('carousel-item-' + dir); // or prev
20455             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20456             cur.el.addClass(lr); // or right
20457             pan.el.addClass(lr);
20458             cur.el.addClass('carousel-item-' +lr); // or right
20459             pan.el.addClass('carousel-item-' +lr);
20460             
20461             
20462             var _this = this;
20463             cur.el.on('transitionend', function() {
20464                 Roo.log("trans end?");
20465                 
20466                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20467                 pan.setActive(true);
20468                 
20469                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20470                 cur.setActive(false);
20471                 
20472                 _this.transition = false;
20473                 
20474             }, this, { single:  true } );
20475             
20476             return true;
20477         }
20478         
20479         cur.setActive(false);
20480         pan.setActive(true);
20481         
20482         return true;
20483         
20484     },
20485     showPanelNext : function()
20486     {
20487         var i = this.indexOfPanel(this.getActivePanel());
20488         
20489         if (i >= this.tabs.length - 1 && !this.autoslide) {
20490             return;
20491         }
20492         
20493         if (i >= this.tabs.length - 1 && this.autoslide) {
20494             i = -1;
20495         }
20496         
20497         this.showPanel(this.tabs[i+1]);
20498     },
20499     
20500     showPanelPrev : function()
20501     {
20502         var i = this.indexOfPanel(this.getActivePanel());
20503         
20504         if (i  < 1 && !this.autoslide) {
20505             return;
20506         }
20507         
20508         if (i < 1 && this.autoslide) {
20509             i = this.tabs.length;
20510         }
20511         
20512         this.showPanel(this.tabs[i-1]);
20513     },
20514     
20515     
20516     addBullet: function()
20517     {
20518         if(!this.bullets || Roo.isTouch){
20519             return;
20520         }
20521         var ctr = this.el.select('.carousel-bullets',true).first();
20522         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20523         var bullet = ctr.createChild({
20524             cls : 'bullet bullet-' + i
20525         },ctr.dom.lastChild);
20526         
20527         
20528         var _this = this;
20529         
20530         bullet.on('click', (function(e, el, o, ii, t){
20531
20532             e.preventDefault();
20533
20534             this.showPanel(ii);
20535
20536             if(this.autoslide && this.slideFn){
20537                 clearInterval(this.slideFn);
20538                 this.slideFn = window.setInterval(function() {
20539                     _this.showPanelNext();
20540                 }, this.timer);
20541             }
20542
20543         }).createDelegate(this, [i, bullet], true));
20544                 
20545         
20546     },
20547      
20548     setActiveBullet : function(i)
20549     {
20550         if(Roo.isTouch){
20551             return;
20552         }
20553         
20554         Roo.each(this.el.select('.bullet', true).elements, function(el){
20555             el.removeClass('selected');
20556         });
20557
20558         var bullet = this.el.select('.bullet-' + i, true).first();
20559         
20560         if(!bullet){
20561             return;
20562         }
20563         
20564         bullet.addClass('selected');
20565     }
20566     
20567     
20568   
20569 });
20570
20571  
20572
20573  
20574  
20575 Roo.apply(Roo.bootstrap.TabGroup, {
20576     
20577     groups: {},
20578      /**
20579     * register a Navigation Group
20580     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20581     */
20582     register : function(navgrp)
20583     {
20584         this.groups[navgrp.navId] = navgrp;
20585         
20586     },
20587     /**
20588     * fetch a Navigation Group based on the navigation ID
20589     * if one does not exist , it will get created.
20590     * @param {string} the navgroup to add
20591     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20592     */
20593     get: function(navId) {
20594         if (typeof(this.groups[navId]) == 'undefined') {
20595             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20596         }
20597         return this.groups[navId] ;
20598     }
20599     
20600     
20601     
20602 });
20603
20604  /*
20605  * - LGPL
20606  *
20607  * TabPanel
20608  * 
20609  */
20610
20611 /**
20612  * @class Roo.bootstrap.TabPanel
20613  * @extends Roo.bootstrap.Component
20614  * Bootstrap TabPanel class
20615  * @cfg {Boolean} active panel active
20616  * @cfg {String} html panel content
20617  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20618  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20619  * @cfg {String} href click to link..
20620  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20621  * 
20622  * 
20623  * @constructor
20624  * Create a new TabPanel
20625  * @param {Object} config The config object
20626  */
20627
20628 Roo.bootstrap.TabPanel = function(config){
20629     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20630     this.addEvents({
20631         /**
20632              * @event changed
20633              * Fires when the active status changes
20634              * @param {Roo.bootstrap.TabPanel} this
20635              * @param {Boolean} state the new state
20636             
20637          */
20638         'changed': true,
20639         /**
20640              * @event beforedeactivate
20641              * Fires before a tab is de-activated - can be used to do validation on a form.
20642              * @param {Roo.bootstrap.TabPanel} this
20643              * @return {Boolean} false if there is an error
20644             
20645          */
20646         'beforedeactivate': true
20647      });
20648     
20649     this.tabId = this.tabId || Roo.id();
20650   
20651 };
20652
20653 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20654     
20655     active: false,
20656     html: false,
20657     tabId: false,
20658     navId : false,
20659     href : '',
20660     touchSlide : false,
20661     getAutoCreate : function(){
20662         
20663         
20664         var cfg = {
20665             tag: 'div',
20666             // item is needed for carousel - not sure if it has any effect otherwise
20667             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20668             html: this.html || ''
20669         };
20670         
20671         if(this.active){
20672             cfg.cls += ' active';
20673         }
20674         
20675         if(this.tabId){
20676             cfg.tabId = this.tabId;
20677         }
20678         
20679         
20680         
20681         return cfg;
20682     },
20683     
20684     initEvents:  function()
20685     {
20686         var p = this.parent();
20687         
20688         this.navId = this.navId || p.navId;
20689         
20690         if (typeof(this.navId) != 'undefined') {
20691             // not really needed.. but just in case.. parent should be a NavGroup.
20692             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20693             
20694             tg.register(this);
20695             
20696             var i = tg.tabs.length - 1;
20697             
20698             if(this.active && tg.bullets > 0 && i < tg.bullets){
20699                 tg.setActiveBullet(i);
20700             }
20701         }
20702         
20703         this.el.on('click', this.onClick, this);
20704         
20705         if(Roo.isTouch && this.touchSlide){
20706             this.el.on("touchstart", this.onTouchStart, this);
20707             this.el.on("touchmove", this.onTouchMove, this);
20708             this.el.on("touchend", this.onTouchEnd, this);
20709         }
20710         
20711     },
20712     
20713     onRender : function(ct, position)
20714     {
20715         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20716     },
20717     
20718     setActive : function(state)
20719     {
20720         Roo.log("panel - set active " + this.tabId + "=" + state);
20721         
20722         this.active = state;
20723         if (!state) {
20724             this.el.removeClass('active');
20725             
20726         } else  if (!this.el.hasClass('active')) {
20727             this.el.addClass('active');
20728         }
20729         
20730         this.fireEvent('changed', this, state);
20731     },
20732     
20733     onClick : function(e)
20734     {
20735         e.preventDefault();
20736         
20737         if(!this.href.length){
20738             return;
20739         }
20740         
20741         window.location.href = this.href;
20742     },
20743     
20744     startX : 0,
20745     startY : 0,
20746     endX : 0,
20747     endY : 0,
20748     swiping : false,
20749     
20750     onTouchStart : function(e)
20751     {
20752         this.swiping = false;
20753         
20754         this.startX = e.browserEvent.touches[0].clientX;
20755         this.startY = e.browserEvent.touches[0].clientY;
20756     },
20757     
20758     onTouchMove : function(e)
20759     {
20760         this.swiping = true;
20761         
20762         this.endX = e.browserEvent.touches[0].clientX;
20763         this.endY = e.browserEvent.touches[0].clientY;
20764     },
20765     
20766     onTouchEnd : function(e)
20767     {
20768         if(!this.swiping){
20769             this.onClick(e);
20770             return;
20771         }
20772         
20773         var tabGroup = this.parent();
20774         
20775         if(this.endX > this.startX){ // swiping right
20776             tabGroup.showPanelPrev();
20777             return;
20778         }
20779         
20780         if(this.startX > this.endX){ // swiping left
20781             tabGroup.showPanelNext();
20782             return;
20783         }
20784     }
20785     
20786     
20787 });
20788  
20789
20790  
20791
20792  /*
20793  * - LGPL
20794  *
20795  * DateField
20796  * 
20797  */
20798
20799 /**
20800  * @class Roo.bootstrap.DateField
20801  * @extends Roo.bootstrap.Input
20802  * Bootstrap DateField class
20803  * @cfg {Number} weekStart default 0
20804  * @cfg {String} viewMode default empty, (months|years)
20805  * @cfg {String} minViewMode default empty, (months|years)
20806  * @cfg {Number} startDate default -Infinity
20807  * @cfg {Number} endDate default Infinity
20808  * @cfg {Boolean} todayHighlight default false
20809  * @cfg {Boolean} todayBtn default false
20810  * @cfg {Boolean} calendarWeeks default false
20811  * @cfg {Object} daysOfWeekDisabled default empty
20812  * @cfg {Boolean} singleMode default false (true | false)
20813  * 
20814  * @cfg {Boolean} keyboardNavigation default true
20815  * @cfg {String} language default en
20816  * 
20817  * @constructor
20818  * Create a new DateField
20819  * @param {Object} config The config object
20820  */
20821
20822 Roo.bootstrap.DateField = function(config){
20823     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20824      this.addEvents({
20825             /**
20826              * @event show
20827              * Fires when this field show.
20828              * @param {Roo.bootstrap.DateField} this
20829              * @param {Mixed} date The date value
20830              */
20831             show : true,
20832             /**
20833              * @event show
20834              * Fires when this field hide.
20835              * @param {Roo.bootstrap.DateField} this
20836              * @param {Mixed} date The date value
20837              */
20838             hide : true,
20839             /**
20840              * @event select
20841              * Fires when select a date.
20842              * @param {Roo.bootstrap.DateField} this
20843              * @param {Mixed} date The date value
20844              */
20845             select : true,
20846             /**
20847              * @event beforeselect
20848              * Fires when before select a date.
20849              * @param {Roo.bootstrap.DateField} this
20850              * @param {Mixed} date The date value
20851              */
20852             beforeselect : true
20853         });
20854 };
20855
20856 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20857     
20858     /**
20859      * @cfg {String} format
20860      * The default date format string which can be overriden for localization support.  The format must be
20861      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20862      */
20863     format : "m/d/y",
20864     /**
20865      * @cfg {String} altFormats
20866      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20867      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20868      */
20869     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20870     
20871     weekStart : 0,
20872     
20873     viewMode : '',
20874     
20875     minViewMode : '',
20876     
20877     todayHighlight : false,
20878     
20879     todayBtn: false,
20880     
20881     language: 'en',
20882     
20883     keyboardNavigation: true,
20884     
20885     calendarWeeks: false,
20886     
20887     startDate: -Infinity,
20888     
20889     endDate: Infinity,
20890     
20891     daysOfWeekDisabled: [],
20892     
20893     _events: [],
20894     
20895     singleMode : false,
20896     
20897     UTCDate: function()
20898     {
20899         return new Date(Date.UTC.apply(Date, arguments));
20900     },
20901     
20902     UTCToday: function()
20903     {
20904         var today = new Date();
20905         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20906     },
20907     
20908     getDate: function() {
20909             var d = this.getUTCDate();
20910             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20911     },
20912     
20913     getUTCDate: function() {
20914             return this.date;
20915     },
20916     
20917     setDate: function(d) {
20918             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20919     },
20920     
20921     setUTCDate: function(d) {
20922             this.date = d;
20923             this.setValue(this.formatDate(this.date));
20924     },
20925         
20926     onRender: function(ct, position)
20927     {
20928         
20929         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20930         
20931         this.language = this.language || 'en';
20932         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20933         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20934         
20935         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20936         this.format = this.format || 'm/d/y';
20937         this.isInline = false;
20938         this.isInput = true;
20939         this.component = this.el.select('.add-on', true).first() || false;
20940         this.component = (this.component && this.component.length === 0) ? false : this.component;
20941         this.hasInput = this.component && this.inputEl().length;
20942         
20943         if (typeof(this.minViewMode === 'string')) {
20944             switch (this.minViewMode) {
20945                 case 'months':
20946                     this.minViewMode = 1;
20947                     break;
20948                 case 'years':
20949                     this.minViewMode = 2;
20950                     break;
20951                 default:
20952                     this.minViewMode = 0;
20953                     break;
20954             }
20955         }
20956         
20957         if (typeof(this.viewMode === 'string')) {
20958             switch (this.viewMode) {
20959                 case 'months':
20960                     this.viewMode = 1;
20961                     break;
20962                 case 'years':
20963                     this.viewMode = 2;
20964                     break;
20965                 default:
20966                     this.viewMode = 0;
20967                     break;
20968             }
20969         }
20970                 
20971         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20972         
20973 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20974         
20975         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20976         
20977         this.picker().on('mousedown', this.onMousedown, this);
20978         this.picker().on('click', this.onClick, this);
20979         
20980         this.picker().addClass('datepicker-dropdown');
20981         
20982         this.startViewMode = this.viewMode;
20983         
20984         if(this.singleMode){
20985             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20986                 v.setVisibilityMode(Roo.Element.DISPLAY);
20987                 v.hide();
20988             });
20989             
20990             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20991                 v.setStyle('width', '189px');
20992             });
20993         }
20994         
20995         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20996             if(!this.calendarWeeks){
20997                 v.remove();
20998                 return;
20999             }
21000             
21001             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21002             v.attr('colspan', function(i, val){
21003                 return parseInt(val) + 1;
21004             });
21005         });
21006                         
21007         
21008         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21009         
21010         this.setStartDate(this.startDate);
21011         this.setEndDate(this.endDate);
21012         
21013         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21014         
21015         this.fillDow();
21016         this.fillMonths();
21017         this.update();
21018         this.showMode();
21019         
21020         if(this.isInline) {
21021             this.showPopup();
21022         }
21023     },
21024     
21025     picker : function()
21026     {
21027         return this.pickerEl;
21028 //        return this.el.select('.datepicker', true).first();
21029     },
21030     
21031     fillDow: function()
21032     {
21033         var dowCnt = this.weekStart;
21034         
21035         var dow = {
21036             tag: 'tr',
21037             cn: [
21038                 
21039             ]
21040         };
21041         
21042         if(this.calendarWeeks){
21043             dow.cn.push({
21044                 tag: 'th',
21045                 cls: 'cw',
21046                 html: '&nbsp;'
21047             })
21048         }
21049         
21050         while (dowCnt < this.weekStart + 7) {
21051             dow.cn.push({
21052                 tag: 'th',
21053                 cls: 'dow',
21054                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21055             });
21056         }
21057         
21058         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21059     },
21060     
21061     fillMonths: function()
21062     {    
21063         var i = 0;
21064         var months = this.picker().select('>.datepicker-months td', true).first();
21065         
21066         months.dom.innerHTML = '';
21067         
21068         while (i < 12) {
21069             var month = {
21070                 tag: 'span',
21071                 cls: 'month',
21072                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21073             };
21074             
21075             months.createChild(month);
21076         }
21077         
21078     },
21079     
21080     update: function()
21081     {
21082         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;
21083         
21084         if (this.date < this.startDate) {
21085             this.viewDate = new Date(this.startDate);
21086         } else if (this.date > this.endDate) {
21087             this.viewDate = new Date(this.endDate);
21088         } else {
21089             this.viewDate = new Date(this.date);
21090         }
21091         
21092         this.fill();
21093     },
21094     
21095     fill: function() 
21096     {
21097         var d = new Date(this.viewDate),
21098                 year = d.getUTCFullYear(),
21099                 month = d.getUTCMonth(),
21100                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21101                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21102                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21103                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21104                 currentDate = this.date && this.date.valueOf(),
21105                 today = this.UTCToday();
21106         
21107         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21108         
21109 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21110         
21111 //        this.picker.select('>tfoot th.today').
21112 //                                              .text(dates[this.language].today)
21113 //                                              .toggle(this.todayBtn !== false);
21114     
21115         this.updateNavArrows();
21116         this.fillMonths();
21117                                                 
21118         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21119         
21120         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21121          
21122         prevMonth.setUTCDate(day);
21123         
21124         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21125         
21126         var nextMonth = new Date(prevMonth);
21127         
21128         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21129         
21130         nextMonth = nextMonth.valueOf();
21131         
21132         var fillMonths = false;
21133         
21134         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21135         
21136         while(prevMonth.valueOf() <= nextMonth) {
21137             var clsName = '';
21138             
21139             if (prevMonth.getUTCDay() === this.weekStart) {
21140                 if(fillMonths){
21141                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21142                 }
21143                     
21144                 fillMonths = {
21145                     tag: 'tr',
21146                     cn: []
21147                 };
21148                 
21149                 if(this.calendarWeeks){
21150                     // ISO 8601: First week contains first thursday.
21151                     // ISO also states week starts on Monday, but we can be more abstract here.
21152                     var
21153                     // Start of current week: based on weekstart/current date
21154                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21155                     // Thursday of this week
21156                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21157                     // First Thursday of year, year from thursday
21158                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21159                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21160                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21161                     
21162                     fillMonths.cn.push({
21163                         tag: 'td',
21164                         cls: 'cw',
21165                         html: calWeek
21166                     });
21167                 }
21168             }
21169             
21170             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21171                 clsName += ' old';
21172             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21173                 clsName += ' new';
21174             }
21175             if (this.todayHighlight &&
21176                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21177                 prevMonth.getUTCMonth() == today.getMonth() &&
21178                 prevMonth.getUTCDate() == today.getDate()) {
21179                 clsName += ' today';
21180             }
21181             
21182             if (currentDate && prevMonth.valueOf() === currentDate) {
21183                 clsName += ' active';
21184             }
21185             
21186             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21187                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21188                     clsName += ' disabled';
21189             }
21190             
21191             fillMonths.cn.push({
21192                 tag: 'td',
21193                 cls: 'day ' + clsName,
21194                 html: prevMonth.getDate()
21195             });
21196             
21197             prevMonth.setDate(prevMonth.getDate()+1);
21198         }
21199           
21200         var currentYear = this.date && this.date.getUTCFullYear();
21201         var currentMonth = this.date && this.date.getUTCMonth();
21202         
21203         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21204         
21205         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21206             v.removeClass('active');
21207             
21208             if(currentYear === year && k === currentMonth){
21209                 v.addClass('active');
21210             }
21211             
21212             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21213                 v.addClass('disabled');
21214             }
21215             
21216         });
21217         
21218         
21219         year = parseInt(year/10, 10) * 10;
21220         
21221         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21222         
21223         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21224         
21225         year -= 1;
21226         for (var i = -1; i < 11; i++) {
21227             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21228                 tag: 'span',
21229                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21230                 html: year
21231             });
21232             
21233             year += 1;
21234         }
21235     },
21236     
21237     showMode: function(dir) 
21238     {
21239         if (dir) {
21240             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21241         }
21242         
21243         Roo.each(this.picker().select('>div',true).elements, function(v){
21244             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21245             v.hide();
21246         });
21247         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21248     },
21249     
21250     place: function()
21251     {
21252         if(this.isInline) {
21253             return;
21254         }
21255         
21256         this.picker().removeClass(['bottom', 'top']);
21257         
21258         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21259             /*
21260              * place to the top of element!
21261              *
21262              */
21263             
21264             this.picker().addClass('top');
21265             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21266             
21267             return;
21268         }
21269         
21270         this.picker().addClass('bottom');
21271         
21272         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21273     },
21274     
21275     parseDate : function(value)
21276     {
21277         if(!value || value instanceof Date){
21278             return value;
21279         }
21280         var v = Date.parseDate(value, this.format);
21281         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21282             v = Date.parseDate(value, 'Y-m-d');
21283         }
21284         if(!v && this.altFormats){
21285             if(!this.altFormatsArray){
21286                 this.altFormatsArray = this.altFormats.split("|");
21287             }
21288             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21289                 v = Date.parseDate(value, this.altFormatsArray[i]);
21290             }
21291         }
21292         return v;
21293     },
21294     
21295     formatDate : function(date, fmt)
21296     {   
21297         return (!date || !(date instanceof Date)) ?
21298         date : date.dateFormat(fmt || this.format);
21299     },
21300     
21301     onFocus : function()
21302     {
21303         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21304         this.showPopup();
21305     },
21306     
21307     onBlur : function()
21308     {
21309         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21310         
21311         var d = this.inputEl().getValue();
21312         
21313         this.setValue(d);
21314                 
21315         this.hidePopup();
21316     },
21317     
21318     showPopup : function()
21319     {
21320         this.picker().show();
21321         this.update();
21322         this.place();
21323         
21324         this.fireEvent('showpopup', this, this.date);
21325     },
21326     
21327     hidePopup : function()
21328     {
21329         if(this.isInline) {
21330             return;
21331         }
21332         this.picker().hide();
21333         this.viewMode = this.startViewMode;
21334         this.showMode();
21335         
21336         this.fireEvent('hidepopup', this, this.date);
21337         
21338     },
21339     
21340     onMousedown: function(e)
21341     {
21342         e.stopPropagation();
21343         e.preventDefault();
21344     },
21345     
21346     keyup: function(e)
21347     {
21348         Roo.bootstrap.DateField.superclass.keyup.call(this);
21349         this.update();
21350     },
21351
21352     setValue: function(v)
21353     {
21354         if(this.fireEvent('beforeselect', this, v) !== false){
21355             var d = new Date(this.parseDate(v) ).clearTime();
21356         
21357             if(isNaN(d.getTime())){
21358                 this.date = this.viewDate = '';
21359                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21360                 return;
21361             }
21362
21363             v = this.formatDate(d);
21364
21365             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21366
21367             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21368
21369             this.update();
21370
21371             this.fireEvent('select', this, this.date);
21372         }
21373     },
21374     
21375     getValue: function()
21376     {
21377         return this.formatDate(this.date);
21378     },
21379     
21380     fireKey: function(e)
21381     {
21382         if (!this.picker().isVisible()){
21383             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21384                 this.showPopup();
21385             }
21386             return;
21387         }
21388         
21389         var dateChanged = false,
21390         dir, day, month,
21391         newDate, newViewDate;
21392         
21393         switch(e.keyCode){
21394             case 27: // escape
21395                 this.hidePopup();
21396                 e.preventDefault();
21397                 break;
21398             case 37: // left
21399             case 39: // right
21400                 if (!this.keyboardNavigation) {
21401                     break;
21402                 }
21403                 dir = e.keyCode == 37 ? -1 : 1;
21404                 
21405                 if (e.ctrlKey){
21406                     newDate = this.moveYear(this.date, dir);
21407                     newViewDate = this.moveYear(this.viewDate, dir);
21408                 } else if (e.shiftKey){
21409                     newDate = this.moveMonth(this.date, dir);
21410                     newViewDate = this.moveMonth(this.viewDate, dir);
21411                 } else {
21412                     newDate = new Date(this.date);
21413                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21414                     newViewDate = new Date(this.viewDate);
21415                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21416                 }
21417                 if (this.dateWithinRange(newDate)){
21418                     this.date = newDate;
21419                     this.viewDate = newViewDate;
21420                     this.setValue(this.formatDate(this.date));
21421 //                    this.update();
21422                     e.preventDefault();
21423                     dateChanged = true;
21424                 }
21425                 break;
21426             case 38: // up
21427             case 40: // down
21428                 if (!this.keyboardNavigation) {
21429                     break;
21430                 }
21431                 dir = e.keyCode == 38 ? -1 : 1;
21432                 if (e.ctrlKey){
21433                     newDate = this.moveYear(this.date, dir);
21434                     newViewDate = this.moveYear(this.viewDate, dir);
21435                 } else if (e.shiftKey){
21436                     newDate = this.moveMonth(this.date, dir);
21437                     newViewDate = this.moveMonth(this.viewDate, dir);
21438                 } else {
21439                     newDate = new Date(this.date);
21440                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21441                     newViewDate = new Date(this.viewDate);
21442                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21443                 }
21444                 if (this.dateWithinRange(newDate)){
21445                     this.date = newDate;
21446                     this.viewDate = newViewDate;
21447                     this.setValue(this.formatDate(this.date));
21448 //                    this.update();
21449                     e.preventDefault();
21450                     dateChanged = true;
21451                 }
21452                 break;
21453             case 13: // enter
21454                 this.setValue(this.formatDate(this.date));
21455                 this.hidePopup();
21456                 e.preventDefault();
21457                 break;
21458             case 9: // tab
21459                 this.setValue(this.formatDate(this.date));
21460                 this.hidePopup();
21461                 break;
21462             case 16: // shift
21463             case 17: // ctrl
21464             case 18: // alt
21465                 break;
21466             default :
21467                 this.hidePopup();
21468                 
21469         }
21470     },
21471     
21472     
21473     onClick: function(e) 
21474     {
21475         e.stopPropagation();
21476         e.preventDefault();
21477         
21478         var target = e.getTarget();
21479         
21480         if(target.nodeName.toLowerCase() === 'i'){
21481             target = Roo.get(target).dom.parentNode;
21482         }
21483         
21484         var nodeName = target.nodeName;
21485         var className = target.className;
21486         var html = target.innerHTML;
21487         //Roo.log(nodeName);
21488         
21489         switch(nodeName.toLowerCase()) {
21490             case 'th':
21491                 switch(className) {
21492                     case 'switch':
21493                         this.showMode(1);
21494                         break;
21495                     case 'prev':
21496                     case 'next':
21497                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21498                         switch(this.viewMode){
21499                                 case 0:
21500                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21501                                         break;
21502                                 case 1:
21503                                 case 2:
21504                                         this.viewDate = this.moveYear(this.viewDate, dir);
21505                                         break;
21506                         }
21507                         this.fill();
21508                         break;
21509                     case 'today':
21510                         var date = new Date();
21511                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21512 //                        this.fill()
21513                         this.setValue(this.formatDate(this.date));
21514                         
21515                         this.hidePopup();
21516                         break;
21517                 }
21518                 break;
21519             case 'span':
21520                 if (className.indexOf('disabled') < 0) {
21521                     this.viewDate.setUTCDate(1);
21522                     if (className.indexOf('month') > -1) {
21523                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21524                     } else {
21525                         var year = parseInt(html, 10) || 0;
21526                         this.viewDate.setUTCFullYear(year);
21527                         
21528                     }
21529                     
21530                     if(this.singleMode){
21531                         this.setValue(this.formatDate(this.viewDate));
21532                         this.hidePopup();
21533                         return;
21534                     }
21535                     
21536                     this.showMode(-1);
21537                     this.fill();
21538                 }
21539                 break;
21540                 
21541             case 'td':
21542                 //Roo.log(className);
21543                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21544                     var day = parseInt(html, 10) || 1;
21545                     var year = this.viewDate.getUTCFullYear(),
21546                         month = this.viewDate.getUTCMonth();
21547
21548                     if (className.indexOf('old') > -1) {
21549                         if(month === 0 ){
21550                             month = 11;
21551                             year -= 1;
21552                         }else{
21553                             month -= 1;
21554                         }
21555                     } else if (className.indexOf('new') > -1) {
21556                         if (month == 11) {
21557                             month = 0;
21558                             year += 1;
21559                         } else {
21560                             month += 1;
21561                         }
21562                     }
21563                     //Roo.log([year,month,day]);
21564                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21565                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21566 //                    this.fill();
21567                     //Roo.log(this.formatDate(this.date));
21568                     this.setValue(this.formatDate(this.date));
21569                     this.hidePopup();
21570                 }
21571                 break;
21572         }
21573     },
21574     
21575     setStartDate: function(startDate)
21576     {
21577         this.startDate = startDate || -Infinity;
21578         if (this.startDate !== -Infinity) {
21579             this.startDate = this.parseDate(this.startDate);
21580         }
21581         this.update();
21582         this.updateNavArrows();
21583     },
21584
21585     setEndDate: function(endDate)
21586     {
21587         this.endDate = endDate || Infinity;
21588         if (this.endDate !== Infinity) {
21589             this.endDate = this.parseDate(this.endDate);
21590         }
21591         this.update();
21592         this.updateNavArrows();
21593     },
21594     
21595     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21596     {
21597         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21598         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21599             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21600         }
21601         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21602             return parseInt(d, 10);
21603         });
21604         this.update();
21605         this.updateNavArrows();
21606     },
21607     
21608     updateNavArrows: function() 
21609     {
21610         if(this.singleMode){
21611             return;
21612         }
21613         
21614         var d = new Date(this.viewDate),
21615         year = d.getUTCFullYear(),
21616         month = d.getUTCMonth();
21617         
21618         Roo.each(this.picker().select('.prev', true).elements, function(v){
21619             v.show();
21620             switch (this.viewMode) {
21621                 case 0:
21622
21623                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21624                         v.hide();
21625                     }
21626                     break;
21627                 case 1:
21628                 case 2:
21629                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21630                         v.hide();
21631                     }
21632                     break;
21633             }
21634         });
21635         
21636         Roo.each(this.picker().select('.next', true).elements, function(v){
21637             v.show();
21638             switch (this.viewMode) {
21639                 case 0:
21640
21641                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21642                         v.hide();
21643                     }
21644                     break;
21645                 case 1:
21646                 case 2:
21647                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21648                         v.hide();
21649                     }
21650                     break;
21651             }
21652         })
21653     },
21654     
21655     moveMonth: function(date, dir)
21656     {
21657         if (!dir) {
21658             return date;
21659         }
21660         var new_date = new Date(date.valueOf()),
21661         day = new_date.getUTCDate(),
21662         month = new_date.getUTCMonth(),
21663         mag = Math.abs(dir),
21664         new_month, test;
21665         dir = dir > 0 ? 1 : -1;
21666         if (mag == 1){
21667             test = dir == -1
21668             // If going back one month, make sure month is not current month
21669             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21670             ? function(){
21671                 return new_date.getUTCMonth() == month;
21672             }
21673             // If going forward one month, make sure month is as expected
21674             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21675             : function(){
21676                 return new_date.getUTCMonth() != new_month;
21677             };
21678             new_month = month + dir;
21679             new_date.setUTCMonth(new_month);
21680             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21681             if (new_month < 0 || new_month > 11) {
21682                 new_month = (new_month + 12) % 12;
21683             }
21684         } else {
21685             // For magnitudes >1, move one month at a time...
21686             for (var i=0; i<mag; i++) {
21687                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21688                 new_date = this.moveMonth(new_date, dir);
21689             }
21690             // ...then reset the day, keeping it in the new month
21691             new_month = new_date.getUTCMonth();
21692             new_date.setUTCDate(day);
21693             test = function(){
21694                 return new_month != new_date.getUTCMonth();
21695             };
21696         }
21697         // Common date-resetting loop -- if date is beyond end of month, make it
21698         // end of month
21699         while (test()){
21700             new_date.setUTCDate(--day);
21701             new_date.setUTCMonth(new_month);
21702         }
21703         return new_date;
21704     },
21705
21706     moveYear: function(date, dir)
21707     {
21708         return this.moveMonth(date, dir*12);
21709     },
21710
21711     dateWithinRange: function(date)
21712     {
21713         return date >= this.startDate && date <= this.endDate;
21714     },
21715
21716     
21717     remove: function() 
21718     {
21719         this.picker().remove();
21720     },
21721     
21722     validateValue : function(value)
21723     {
21724         if(this.getVisibilityEl().hasClass('hidden')){
21725             return true;
21726         }
21727         
21728         if(value.length < 1)  {
21729             if(this.allowBlank){
21730                 return true;
21731             }
21732             return false;
21733         }
21734         
21735         if(value.length < this.minLength){
21736             return false;
21737         }
21738         if(value.length > this.maxLength){
21739             return false;
21740         }
21741         if(this.vtype){
21742             var vt = Roo.form.VTypes;
21743             if(!vt[this.vtype](value, this)){
21744                 return false;
21745             }
21746         }
21747         if(typeof this.validator == "function"){
21748             var msg = this.validator(value);
21749             if(msg !== true){
21750                 return false;
21751             }
21752         }
21753         
21754         if(this.regex && !this.regex.test(value)){
21755             return false;
21756         }
21757         
21758         if(typeof(this.parseDate(value)) == 'undefined'){
21759             return false;
21760         }
21761         
21762         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21763             return false;
21764         }      
21765         
21766         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21767             return false;
21768         } 
21769         
21770         
21771         return true;
21772     },
21773     
21774     reset : function()
21775     {
21776         this.date = this.viewDate = '';
21777         
21778         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21779     }
21780    
21781 });
21782
21783 Roo.apply(Roo.bootstrap.DateField,  {
21784     
21785     head : {
21786         tag: 'thead',
21787         cn: [
21788         {
21789             tag: 'tr',
21790             cn: [
21791             {
21792                 tag: 'th',
21793                 cls: 'prev',
21794                 html: '<i class="fa fa-arrow-left"/>'
21795             },
21796             {
21797                 tag: 'th',
21798                 cls: 'switch',
21799                 colspan: '5'
21800             },
21801             {
21802                 tag: 'th',
21803                 cls: 'next',
21804                 html: '<i class="fa fa-arrow-right"/>'
21805             }
21806
21807             ]
21808         }
21809         ]
21810     },
21811     
21812     content : {
21813         tag: 'tbody',
21814         cn: [
21815         {
21816             tag: 'tr',
21817             cn: [
21818             {
21819                 tag: 'td',
21820                 colspan: '7'
21821             }
21822             ]
21823         }
21824         ]
21825     },
21826     
21827     footer : {
21828         tag: 'tfoot',
21829         cn: [
21830         {
21831             tag: 'tr',
21832             cn: [
21833             {
21834                 tag: 'th',
21835                 colspan: '7',
21836                 cls: 'today'
21837             }
21838                     
21839             ]
21840         }
21841         ]
21842     },
21843     
21844     dates:{
21845         en: {
21846             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21847             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21848             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21849             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21850             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21851             today: "Today"
21852         }
21853     },
21854     
21855     modes: [
21856     {
21857         clsName: 'days',
21858         navFnc: 'Month',
21859         navStep: 1
21860     },
21861     {
21862         clsName: 'months',
21863         navFnc: 'FullYear',
21864         navStep: 1
21865     },
21866     {
21867         clsName: 'years',
21868         navFnc: 'FullYear',
21869         navStep: 10
21870     }]
21871 });
21872
21873 Roo.apply(Roo.bootstrap.DateField,  {
21874   
21875     template : {
21876         tag: 'div',
21877         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21878         cn: [
21879         {
21880             tag: 'div',
21881             cls: 'datepicker-days',
21882             cn: [
21883             {
21884                 tag: 'table',
21885                 cls: 'table-condensed',
21886                 cn:[
21887                 Roo.bootstrap.DateField.head,
21888                 {
21889                     tag: 'tbody'
21890                 },
21891                 Roo.bootstrap.DateField.footer
21892                 ]
21893             }
21894             ]
21895         },
21896         {
21897             tag: 'div',
21898             cls: 'datepicker-months',
21899             cn: [
21900             {
21901                 tag: 'table',
21902                 cls: 'table-condensed',
21903                 cn:[
21904                 Roo.bootstrap.DateField.head,
21905                 Roo.bootstrap.DateField.content,
21906                 Roo.bootstrap.DateField.footer
21907                 ]
21908             }
21909             ]
21910         },
21911         {
21912             tag: 'div',
21913             cls: 'datepicker-years',
21914             cn: [
21915             {
21916                 tag: 'table',
21917                 cls: 'table-condensed',
21918                 cn:[
21919                 Roo.bootstrap.DateField.head,
21920                 Roo.bootstrap.DateField.content,
21921                 Roo.bootstrap.DateField.footer
21922                 ]
21923             }
21924             ]
21925         }
21926         ]
21927     }
21928 });
21929
21930  
21931
21932  /*
21933  * - LGPL
21934  *
21935  * TimeField
21936  * 
21937  */
21938
21939 /**
21940  * @class Roo.bootstrap.TimeField
21941  * @extends Roo.bootstrap.Input
21942  * Bootstrap DateField class
21943  * 
21944  * 
21945  * @constructor
21946  * Create a new TimeField
21947  * @param {Object} config The config object
21948  */
21949
21950 Roo.bootstrap.TimeField = function(config){
21951     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21952     this.addEvents({
21953             /**
21954              * @event show
21955              * Fires when this field show.
21956              * @param {Roo.bootstrap.DateField} thisthis
21957              * @param {Mixed} date The date value
21958              */
21959             show : true,
21960             /**
21961              * @event show
21962              * Fires when this field hide.
21963              * @param {Roo.bootstrap.DateField} this
21964              * @param {Mixed} date The date value
21965              */
21966             hide : true,
21967             /**
21968              * @event select
21969              * Fires when select a date.
21970              * @param {Roo.bootstrap.DateField} this
21971              * @param {Mixed} date The date value
21972              */
21973             select : true
21974         });
21975 };
21976
21977 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21978     
21979     /**
21980      * @cfg {String} format
21981      * The default time format string which can be overriden for localization support.  The format must be
21982      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21983      */
21984     format : "H:i",
21985
21986     getAutoCreate : function()
21987     {
21988         this.after = '<i class="fa far fa-clock"></i>';
21989         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
21990         
21991          
21992     },
21993     onRender: function(ct, position)
21994     {
21995         
21996         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21997                 
21998         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
21999         
22000         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22001         
22002         this.pop = this.picker().select('>.datepicker-time',true).first();
22003         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22004         
22005         this.picker().on('mousedown', this.onMousedown, this);
22006         this.picker().on('click', this.onClick, this);
22007         
22008         this.picker().addClass('datepicker-dropdown');
22009     
22010         this.fillTime();
22011         this.update();
22012             
22013         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22014         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22015         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22016         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22017         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22018         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22019
22020     },
22021     
22022     fireKey: function(e){
22023         if (!this.picker().isVisible()){
22024             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22025                 this.show();
22026             }
22027             return;
22028         }
22029
22030         e.preventDefault();
22031         
22032         switch(e.keyCode){
22033             case 27: // escape
22034                 this.hide();
22035                 break;
22036             case 37: // left
22037             case 39: // right
22038                 this.onTogglePeriod();
22039                 break;
22040             case 38: // up
22041                 this.onIncrementMinutes();
22042                 break;
22043             case 40: // down
22044                 this.onDecrementMinutes();
22045                 break;
22046             case 13: // enter
22047             case 9: // tab
22048                 this.setTime();
22049                 break;
22050         }
22051     },
22052     
22053     onClick: function(e) {
22054         e.stopPropagation();
22055         e.preventDefault();
22056     },
22057     
22058     picker : function()
22059     {
22060         return this.pickerEl;
22061     },
22062     
22063     fillTime: function()
22064     {    
22065         var time = this.pop.select('tbody', true).first();
22066         
22067         time.dom.innerHTML = '';
22068         
22069         time.createChild({
22070             tag: 'tr',
22071             cn: [
22072                 {
22073                     tag: 'td',
22074                     cn: [
22075                         {
22076                             tag: 'a',
22077                             href: '#',
22078                             cls: 'btn',
22079                             cn: [
22080                                 {
22081                                     tag: 'i',
22082                                     cls: 'hours-up fa fas fa-chevron-up'
22083                                 }
22084                             ]
22085                         } 
22086                     ]
22087                 },
22088                 {
22089                     tag: 'td',
22090                     cls: 'separator'
22091                 },
22092                 {
22093                     tag: 'td',
22094                     cn: [
22095                         {
22096                             tag: 'a',
22097                             href: '#',
22098                             cls: 'btn',
22099                             cn: [
22100                                 {
22101                                     tag: 'i',
22102                                     cls: 'minutes-up fa fas fa-chevron-up'
22103                                 }
22104                             ]
22105                         }
22106                     ]
22107                 },
22108                 {
22109                     tag: 'td',
22110                     cls: 'separator'
22111                 }
22112             ]
22113         });
22114         
22115         time.createChild({
22116             tag: 'tr',
22117             cn: [
22118                 {
22119                     tag: 'td',
22120                     cn: [
22121                         {
22122                             tag: 'span',
22123                             cls: 'timepicker-hour',
22124                             html: '00'
22125                         }  
22126                     ]
22127                 },
22128                 {
22129                     tag: 'td',
22130                     cls: 'separator',
22131                     html: ':'
22132                 },
22133                 {
22134                     tag: 'td',
22135                     cn: [
22136                         {
22137                             tag: 'span',
22138                             cls: 'timepicker-minute',
22139                             html: '00'
22140                         }  
22141                     ]
22142                 },
22143                 {
22144                     tag: 'td',
22145                     cls: 'separator'
22146                 },
22147                 {
22148                     tag: 'td',
22149                     cn: [
22150                         {
22151                             tag: 'button',
22152                             type: 'button',
22153                             cls: 'btn btn-primary period',
22154                             html: 'AM'
22155                             
22156                         }
22157                     ]
22158                 }
22159             ]
22160         });
22161         
22162         time.createChild({
22163             tag: 'tr',
22164             cn: [
22165                 {
22166                     tag: 'td',
22167                     cn: [
22168                         {
22169                             tag: 'a',
22170                             href: '#',
22171                             cls: 'btn',
22172                             cn: [
22173                                 {
22174                                     tag: 'span',
22175                                     cls: 'hours-down fa fas fa-chevron-down'
22176                                 }
22177                             ]
22178                         }
22179                     ]
22180                 },
22181                 {
22182                     tag: 'td',
22183                     cls: 'separator'
22184                 },
22185                 {
22186                     tag: 'td',
22187                     cn: [
22188                         {
22189                             tag: 'a',
22190                             href: '#',
22191                             cls: 'btn',
22192                             cn: [
22193                                 {
22194                                     tag: 'span',
22195                                     cls: 'minutes-down fa fas fa-chevron-down'
22196                                 }
22197                             ]
22198                         }
22199                     ]
22200                 },
22201                 {
22202                     tag: 'td',
22203                     cls: 'separator'
22204                 }
22205             ]
22206         });
22207         
22208     },
22209     
22210     update: function()
22211     {
22212         
22213         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22214         
22215         this.fill();
22216     },
22217     
22218     fill: function() 
22219     {
22220         var hours = this.time.getHours();
22221         var minutes = this.time.getMinutes();
22222         var period = 'AM';
22223         
22224         if(hours > 11){
22225             period = 'PM';
22226         }
22227         
22228         if(hours == 0){
22229             hours = 12;
22230         }
22231         
22232         
22233         if(hours > 12){
22234             hours = hours - 12;
22235         }
22236         
22237         if(hours < 10){
22238             hours = '0' + hours;
22239         }
22240         
22241         if(minutes < 10){
22242             minutes = '0' + minutes;
22243         }
22244         
22245         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22246         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22247         this.pop.select('button', true).first().dom.innerHTML = period;
22248         
22249     },
22250     
22251     place: function()
22252     {   
22253         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22254         
22255         var cls = ['bottom'];
22256         
22257         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22258             cls.pop();
22259             cls.push('top');
22260         }
22261         
22262         cls.push('right');
22263         
22264         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22265             cls.pop();
22266             cls.push('left');
22267         }
22268         //this.picker().setXY(20000,20000);
22269         this.picker().addClass(cls.join('-'));
22270         
22271         var _this = this;
22272         
22273         Roo.each(cls, function(c){
22274             if(c == 'bottom'){
22275                 (function() {
22276                  //  
22277                 }).defer(200);
22278                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22279                 //_this.picker().setTop(_this.inputEl().getHeight());
22280                 return;
22281             }
22282             if(c == 'top'){
22283                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22284                 
22285                 //_this.picker().setTop(0 - _this.picker().getHeight());
22286                 return;
22287             }
22288             /*
22289             if(c == 'left'){
22290                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22291                 return;
22292             }
22293             if(c == 'right'){
22294                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22295                 return;
22296             }
22297             */
22298         });
22299         
22300     },
22301   
22302     onFocus : function()
22303     {
22304         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22305         this.show();
22306     },
22307     
22308     onBlur : function()
22309     {
22310         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22311         this.hide();
22312     },
22313     
22314     show : function()
22315     {
22316         this.picker().show();
22317         this.pop.show();
22318         this.update();
22319         this.place();
22320         
22321         this.fireEvent('show', this, this.date);
22322     },
22323     
22324     hide : function()
22325     {
22326         this.picker().hide();
22327         this.pop.hide();
22328         
22329         this.fireEvent('hide', this, this.date);
22330     },
22331     
22332     setTime : function()
22333     {
22334         this.hide();
22335         this.setValue(this.time.format(this.format));
22336         
22337         this.fireEvent('select', this, this.date);
22338         
22339         
22340     },
22341     
22342     onMousedown: function(e){
22343         e.stopPropagation();
22344         e.preventDefault();
22345     },
22346     
22347     onIncrementHours: function()
22348     {
22349         Roo.log('onIncrementHours');
22350         this.time = this.time.add(Date.HOUR, 1);
22351         this.update();
22352         
22353     },
22354     
22355     onDecrementHours: function()
22356     {
22357         Roo.log('onDecrementHours');
22358         this.time = this.time.add(Date.HOUR, -1);
22359         this.update();
22360     },
22361     
22362     onIncrementMinutes: function()
22363     {
22364         Roo.log('onIncrementMinutes');
22365         this.time = this.time.add(Date.MINUTE, 1);
22366         this.update();
22367     },
22368     
22369     onDecrementMinutes: function()
22370     {
22371         Roo.log('onDecrementMinutes');
22372         this.time = this.time.add(Date.MINUTE, -1);
22373         this.update();
22374     },
22375     
22376     onTogglePeriod: function()
22377     {
22378         Roo.log('onTogglePeriod');
22379         this.time = this.time.add(Date.HOUR, 12);
22380         this.update();
22381     }
22382     
22383    
22384 });
22385  
22386
22387 Roo.apply(Roo.bootstrap.TimeField,  {
22388   
22389     template : {
22390         tag: 'div',
22391         cls: 'datepicker dropdown-menu',
22392         cn: [
22393             {
22394                 tag: 'div',
22395                 cls: 'datepicker-time',
22396                 cn: [
22397                 {
22398                     tag: 'table',
22399                     cls: 'table-condensed',
22400                     cn:[
22401                         {
22402                             tag: 'tbody',
22403                             cn: [
22404                                 {
22405                                     tag: 'tr',
22406                                     cn: [
22407                                     {
22408                                         tag: 'td',
22409                                         colspan: '7'
22410                                     }
22411                                     ]
22412                                 }
22413                             ]
22414                         },
22415                         {
22416                             tag: 'tfoot',
22417                             cn: [
22418                                 {
22419                                     tag: 'tr',
22420                                     cn: [
22421                                     {
22422                                         tag: 'th',
22423                                         colspan: '7',
22424                                         cls: '',
22425                                         cn: [
22426                                             {
22427                                                 tag: 'button',
22428                                                 cls: 'btn btn-info ok',
22429                                                 html: 'OK'
22430                                             }
22431                                         ]
22432                                     }
22433                     
22434                                     ]
22435                                 }
22436                             ]
22437                         }
22438                     ]
22439                 }
22440                 ]
22441             }
22442         ]
22443     }
22444 });
22445
22446  
22447
22448  /*
22449  * - LGPL
22450  *
22451  * MonthField
22452  * 
22453  */
22454
22455 /**
22456  * @class Roo.bootstrap.MonthField
22457  * @extends Roo.bootstrap.Input
22458  * Bootstrap MonthField class
22459  * 
22460  * @cfg {String} language default en
22461  * 
22462  * @constructor
22463  * Create a new MonthField
22464  * @param {Object} config The config object
22465  */
22466
22467 Roo.bootstrap.MonthField = function(config){
22468     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22469     
22470     this.addEvents({
22471         /**
22472          * @event show
22473          * Fires when this field show.
22474          * @param {Roo.bootstrap.MonthField} this
22475          * @param {Mixed} date The date value
22476          */
22477         show : true,
22478         /**
22479          * @event show
22480          * Fires when this field hide.
22481          * @param {Roo.bootstrap.MonthField} this
22482          * @param {Mixed} date The date value
22483          */
22484         hide : true,
22485         /**
22486          * @event select
22487          * Fires when select a date.
22488          * @param {Roo.bootstrap.MonthField} this
22489          * @param {String} oldvalue The old value
22490          * @param {String} newvalue The new value
22491          */
22492         select : true
22493     });
22494 };
22495
22496 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22497     
22498     onRender: function(ct, position)
22499     {
22500         
22501         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22502         
22503         this.language = this.language || 'en';
22504         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22505         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22506         
22507         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22508         this.isInline = false;
22509         this.isInput = true;
22510         this.component = this.el.select('.add-on', true).first() || false;
22511         this.component = (this.component && this.component.length === 0) ? false : this.component;
22512         this.hasInput = this.component && this.inputEL().length;
22513         
22514         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22515         
22516         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22517         
22518         this.picker().on('mousedown', this.onMousedown, this);
22519         this.picker().on('click', this.onClick, this);
22520         
22521         this.picker().addClass('datepicker-dropdown');
22522         
22523         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22524             v.setStyle('width', '189px');
22525         });
22526         
22527         this.fillMonths();
22528         
22529         this.update();
22530         
22531         if(this.isInline) {
22532             this.show();
22533         }
22534         
22535     },
22536     
22537     setValue: function(v, suppressEvent)
22538     {   
22539         var o = this.getValue();
22540         
22541         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22542         
22543         this.update();
22544
22545         if(suppressEvent !== true){
22546             this.fireEvent('select', this, o, v);
22547         }
22548         
22549     },
22550     
22551     getValue: function()
22552     {
22553         return this.value;
22554     },
22555     
22556     onClick: function(e) 
22557     {
22558         e.stopPropagation();
22559         e.preventDefault();
22560         
22561         var target = e.getTarget();
22562         
22563         if(target.nodeName.toLowerCase() === 'i'){
22564             target = Roo.get(target).dom.parentNode;
22565         }
22566         
22567         var nodeName = target.nodeName;
22568         var className = target.className;
22569         var html = target.innerHTML;
22570         
22571         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22572             return;
22573         }
22574         
22575         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22576         
22577         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22578         
22579         this.hide();
22580                         
22581     },
22582     
22583     picker : function()
22584     {
22585         return this.pickerEl;
22586     },
22587     
22588     fillMonths: function()
22589     {    
22590         var i = 0;
22591         var months = this.picker().select('>.datepicker-months td', true).first();
22592         
22593         months.dom.innerHTML = '';
22594         
22595         while (i < 12) {
22596             var month = {
22597                 tag: 'span',
22598                 cls: 'month',
22599                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22600             };
22601             
22602             months.createChild(month);
22603         }
22604         
22605     },
22606     
22607     update: function()
22608     {
22609         var _this = this;
22610         
22611         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22612             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22613         }
22614         
22615         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22616             e.removeClass('active');
22617             
22618             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22619                 e.addClass('active');
22620             }
22621         })
22622     },
22623     
22624     place: function()
22625     {
22626         if(this.isInline) {
22627             return;
22628         }
22629         
22630         this.picker().removeClass(['bottom', 'top']);
22631         
22632         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22633             /*
22634              * place to the top of element!
22635              *
22636              */
22637             
22638             this.picker().addClass('top');
22639             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22640             
22641             return;
22642         }
22643         
22644         this.picker().addClass('bottom');
22645         
22646         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22647     },
22648     
22649     onFocus : function()
22650     {
22651         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22652         this.show();
22653     },
22654     
22655     onBlur : function()
22656     {
22657         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22658         
22659         var d = this.inputEl().getValue();
22660         
22661         this.setValue(d);
22662                 
22663         this.hide();
22664     },
22665     
22666     show : function()
22667     {
22668         this.picker().show();
22669         this.picker().select('>.datepicker-months', true).first().show();
22670         this.update();
22671         this.place();
22672         
22673         this.fireEvent('show', this, this.date);
22674     },
22675     
22676     hide : function()
22677     {
22678         if(this.isInline) {
22679             return;
22680         }
22681         this.picker().hide();
22682         this.fireEvent('hide', this, this.date);
22683         
22684     },
22685     
22686     onMousedown: function(e)
22687     {
22688         e.stopPropagation();
22689         e.preventDefault();
22690     },
22691     
22692     keyup: function(e)
22693     {
22694         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22695         this.update();
22696     },
22697
22698     fireKey: function(e)
22699     {
22700         if (!this.picker().isVisible()){
22701             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22702                 this.show();
22703             }
22704             return;
22705         }
22706         
22707         var dir;
22708         
22709         switch(e.keyCode){
22710             case 27: // escape
22711                 this.hide();
22712                 e.preventDefault();
22713                 break;
22714             case 37: // left
22715             case 39: // right
22716                 dir = e.keyCode == 37 ? -1 : 1;
22717                 
22718                 this.vIndex = this.vIndex + dir;
22719                 
22720                 if(this.vIndex < 0){
22721                     this.vIndex = 0;
22722                 }
22723                 
22724                 if(this.vIndex > 11){
22725                     this.vIndex = 11;
22726                 }
22727                 
22728                 if(isNaN(this.vIndex)){
22729                     this.vIndex = 0;
22730                 }
22731                 
22732                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22733                 
22734                 break;
22735             case 38: // up
22736             case 40: // down
22737                 
22738                 dir = e.keyCode == 38 ? -1 : 1;
22739                 
22740                 this.vIndex = this.vIndex + dir * 4;
22741                 
22742                 if(this.vIndex < 0){
22743                     this.vIndex = 0;
22744                 }
22745                 
22746                 if(this.vIndex > 11){
22747                     this.vIndex = 11;
22748                 }
22749                 
22750                 if(isNaN(this.vIndex)){
22751                     this.vIndex = 0;
22752                 }
22753                 
22754                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22755                 break;
22756                 
22757             case 13: // enter
22758                 
22759                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22760                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22761                 }
22762                 
22763                 this.hide();
22764                 e.preventDefault();
22765                 break;
22766             case 9: // tab
22767                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22768                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22769                 }
22770                 this.hide();
22771                 break;
22772             case 16: // shift
22773             case 17: // ctrl
22774             case 18: // alt
22775                 break;
22776             default :
22777                 this.hide();
22778                 
22779         }
22780     },
22781     
22782     remove: function() 
22783     {
22784         this.picker().remove();
22785     }
22786    
22787 });
22788
22789 Roo.apply(Roo.bootstrap.MonthField,  {
22790     
22791     content : {
22792         tag: 'tbody',
22793         cn: [
22794         {
22795             tag: 'tr',
22796             cn: [
22797             {
22798                 tag: 'td',
22799                 colspan: '7'
22800             }
22801             ]
22802         }
22803         ]
22804     },
22805     
22806     dates:{
22807         en: {
22808             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22809             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22810         }
22811     }
22812 });
22813
22814 Roo.apply(Roo.bootstrap.MonthField,  {
22815   
22816     template : {
22817         tag: 'div',
22818         cls: 'datepicker dropdown-menu roo-dynamic',
22819         cn: [
22820             {
22821                 tag: 'div',
22822                 cls: 'datepicker-months',
22823                 cn: [
22824                 {
22825                     tag: 'table',
22826                     cls: 'table-condensed',
22827                     cn:[
22828                         Roo.bootstrap.DateField.content
22829                     ]
22830                 }
22831                 ]
22832             }
22833         ]
22834     }
22835 });
22836
22837  
22838
22839  
22840  /*
22841  * - LGPL
22842  *
22843  * CheckBox
22844  * 
22845  */
22846
22847 /**
22848  * @class Roo.bootstrap.CheckBox
22849  * @extends Roo.bootstrap.Input
22850  * Bootstrap CheckBox class
22851  * 
22852  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22853  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22854  * @cfg {String} boxLabel The text that appears beside the checkbox
22855  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22856  * @cfg {Boolean} checked initnal the element
22857  * @cfg {Boolean} inline inline the element (default false)
22858  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22859  * @cfg {String} tooltip label tooltip
22860  * 
22861  * @constructor
22862  * Create a new CheckBox
22863  * @param {Object} config The config object
22864  */
22865
22866 Roo.bootstrap.CheckBox = function(config){
22867     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22868    
22869     this.addEvents({
22870         /**
22871         * @event check
22872         * Fires when the element is checked or unchecked.
22873         * @param {Roo.bootstrap.CheckBox} this This input
22874         * @param {Boolean} checked The new checked value
22875         */
22876        check : true,
22877        /**
22878         * @event click
22879         * Fires when the element is click.
22880         * @param {Roo.bootstrap.CheckBox} this This input
22881         */
22882        click : true
22883     });
22884     
22885 };
22886
22887 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22888   
22889     inputType: 'checkbox',
22890     inputValue: 1,
22891     valueOff: 0,
22892     boxLabel: false,
22893     checked: false,
22894     weight : false,
22895     inline: false,
22896     tooltip : '',
22897     
22898     // checkbox success does not make any sense really.. 
22899     invalidClass : "",
22900     validClass : "",
22901     
22902     
22903     getAutoCreate : function()
22904     {
22905         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22906         
22907         var id = Roo.id();
22908         
22909         var cfg = {};
22910         
22911         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22912         
22913         if(this.inline){
22914             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22915         }
22916         
22917         var input =  {
22918             tag: 'input',
22919             id : id,
22920             type : this.inputType,
22921             value : this.inputValue,
22922             cls : 'roo-' + this.inputType, //'form-box',
22923             placeholder : this.placeholder || ''
22924             
22925         };
22926         
22927         if(this.inputType != 'radio'){
22928             var hidden =  {
22929                 tag: 'input',
22930                 type : 'hidden',
22931                 cls : 'roo-hidden-value',
22932                 value : this.checked ? this.inputValue : this.valueOff
22933             };
22934         }
22935         
22936             
22937         if (this.weight) { // Validity check?
22938             cfg.cls += " " + this.inputType + "-" + this.weight;
22939         }
22940         
22941         if (this.disabled) {
22942             input.disabled=true;
22943         }
22944         
22945         if(this.checked){
22946             input.checked = this.checked;
22947         }
22948         
22949         if (this.name) {
22950             
22951             input.name = this.name;
22952             
22953             if(this.inputType != 'radio'){
22954                 hidden.name = this.name;
22955                 input.name = '_hidden_' + this.name;
22956             }
22957         }
22958         
22959         if (this.size) {
22960             input.cls += ' input-' + this.size;
22961         }
22962         
22963         var settings=this;
22964         
22965         ['xs','sm','md','lg'].map(function(size){
22966             if (settings[size]) {
22967                 cfg.cls += ' col-' + size + '-' + settings[size];
22968             }
22969         });
22970         
22971         var inputblock = input;
22972          
22973         if (this.before || this.after) {
22974             
22975             inputblock = {
22976                 cls : 'input-group',
22977                 cn :  [] 
22978             };
22979             
22980             if (this.before) {
22981                 inputblock.cn.push({
22982                     tag :'span',
22983                     cls : 'input-group-addon',
22984                     html : this.before
22985                 });
22986             }
22987             
22988             inputblock.cn.push(input);
22989             
22990             if(this.inputType != 'radio'){
22991                 inputblock.cn.push(hidden);
22992             }
22993             
22994             if (this.after) {
22995                 inputblock.cn.push({
22996                     tag :'span',
22997                     cls : 'input-group-addon',
22998                     html : this.after
22999                 });
23000             }
23001             
23002         }
23003         var boxLabelCfg = false;
23004         
23005         if(this.boxLabel){
23006            
23007             boxLabelCfg = {
23008                 tag: 'label',
23009                 //'for': id, // box label is handled by onclick - so no for...
23010                 cls: 'box-label',
23011                 html: this.boxLabel
23012             };
23013             if(this.tooltip){
23014                 boxLabelCfg.tooltip = this.tooltip;
23015             }
23016              
23017         }
23018         
23019         
23020         if (align ==='left' && this.fieldLabel.length) {
23021 //                Roo.log("left and has label");
23022             cfg.cn = [
23023                 {
23024                     tag: 'label',
23025                     'for' :  id,
23026                     cls : 'control-label',
23027                     html : this.fieldLabel
23028                 },
23029                 {
23030                     cls : "", 
23031                     cn: [
23032                         inputblock
23033                     ]
23034                 }
23035             ];
23036             
23037             if (boxLabelCfg) {
23038                 cfg.cn[1].cn.push(boxLabelCfg);
23039             }
23040             
23041             if(this.labelWidth > 12){
23042                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23043             }
23044             
23045             if(this.labelWidth < 13 && this.labelmd == 0){
23046                 this.labelmd = this.labelWidth;
23047             }
23048             
23049             if(this.labellg > 0){
23050                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23051                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23052             }
23053             
23054             if(this.labelmd > 0){
23055                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23056                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23057             }
23058             
23059             if(this.labelsm > 0){
23060                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23061                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23062             }
23063             
23064             if(this.labelxs > 0){
23065                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23066                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23067             }
23068             
23069         } else if ( this.fieldLabel.length) {
23070 //                Roo.log(" label");
23071                 cfg.cn = [
23072                    
23073                     {
23074                         tag: this.boxLabel ? 'span' : 'label',
23075                         'for': id,
23076                         cls: 'control-label box-input-label',
23077                         //cls : 'input-group-addon',
23078                         html : this.fieldLabel
23079                     },
23080                     
23081                     inputblock
23082                     
23083                 ];
23084                 if (boxLabelCfg) {
23085                     cfg.cn.push(boxLabelCfg);
23086                 }
23087
23088         } else {
23089             
23090 //                Roo.log(" no label && no align");
23091                 cfg.cn = [  inputblock ] ;
23092                 if (boxLabelCfg) {
23093                     cfg.cn.push(boxLabelCfg);
23094                 }
23095
23096                 
23097         }
23098         
23099        
23100         
23101         if(this.inputType != 'radio'){
23102             cfg.cn.push(hidden);
23103         }
23104         
23105         return cfg;
23106         
23107     },
23108     
23109     /**
23110      * return the real input element.
23111      */
23112     inputEl: function ()
23113     {
23114         return this.el.select('input.roo-' + this.inputType,true).first();
23115     },
23116     hiddenEl: function ()
23117     {
23118         return this.el.select('input.roo-hidden-value',true).first();
23119     },
23120     
23121     labelEl: function()
23122     {
23123         return this.el.select('label.control-label',true).first();
23124     },
23125     /* depricated... */
23126     
23127     label: function()
23128     {
23129         return this.labelEl();
23130     },
23131     
23132     boxLabelEl: function()
23133     {
23134         return this.el.select('label.box-label',true).first();
23135     },
23136     
23137     initEvents : function()
23138     {
23139 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23140         
23141         this.inputEl().on('click', this.onClick,  this);
23142         
23143         if (this.boxLabel) { 
23144             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23145         }
23146         
23147         this.startValue = this.getValue();
23148         
23149         if(this.groupId){
23150             Roo.bootstrap.CheckBox.register(this);
23151         }
23152     },
23153     
23154     onClick : function(e)
23155     {   
23156         if(this.fireEvent('click', this, e) !== false){
23157             this.setChecked(!this.checked);
23158         }
23159         
23160     },
23161     
23162     setChecked : function(state,suppressEvent)
23163     {
23164         this.startValue = this.getValue();
23165
23166         if(this.inputType == 'radio'){
23167             
23168             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23169                 e.dom.checked = false;
23170             });
23171             
23172             this.inputEl().dom.checked = true;
23173             
23174             this.inputEl().dom.value = this.inputValue;
23175             
23176             if(suppressEvent !== true){
23177                 this.fireEvent('check', this, true);
23178             }
23179             
23180             this.validate();
23181             
23182             return;
23183         }
23184         
23185         this.checked = state;
23186         
23187         this.inputEl().dom.checked = state;
23188         
23189         
23190         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23191         
23192         if(suppressEvent !== true){
23193             this.fireEvent('check', this, state);
23194         }
23195         
23196         this.validate();
23197     },
23198     
23199     getValue : function()
23200     {
23201         if(this.inputType == 'radio'){
23202             return this.getGroupValue();
23203         }
23204         
23205         return this.hiddenEl().dom.value;
23206         
23207     },
23208     
23209     getGroupValue : function()
23210     {
23211         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23212             return '';
23213         }
23214         
23215         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23216     },
23217     
23218     setValue : function(v,suppressEvent)
23219     {
23220         if(this.inputType == 'radio'){
23221             this.setGroupValue(v, suppressEvent);
23222             return;
23223         }
23224         
23225         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23226         
23227         this.validate();
23228     },
23229     
23230     setGroupValue : function(v, suppressEvent)
23231     {
23232         this.startValue = this.getValue();
23233         
23234         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23235             e.dom.checked = false;
23236             
23237             if(e.dom.value == v){
23238                 e.dom.checked = true;
23239             }
23240         });
23241         
23242         if(suppressEvent !== true){
23243             this.fireEvent('check', this, true);
23244         }
23245
23246         this.validate();
23247         
23248         return;
23249     },
23250     
23251     validate : function()
23252     {
23253         if(this.getVisibilityEl().hasClass('hidden')){
23254             return true;
23255         }
23256         
23257         if(
23258                 this.disabled || 
23259                 (this.inputType == 'radio' && this.validateRadio()) ||
23260                 (this.inputType == 'checkbox' && this.validateCheckbox())
23261         ){
23262             this.markValid();
23263             return true;
23264         }
23265         
23266         this.markInvalid();
23267         return false;
23268     },
23269     
23270     validateRadio : function()
23271     {
23272         if(this.getVisibilityEl().hasClass('hidden')){
23273             return true;
23274         }
23275         
23276         if(this.allowBlank){
23277             return true;
23278         }
23279         
23280         var valid = false;
23281         
23282         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23283             if(!e.dom.checked){
23284                 return;
23285             }
23286             
23287             valid = true;
23288             
23289             return false;
23290         });
23291         
23292         return valid;
23293     },
23294     
23295     validateCheckbox : function()
23296     {
23297         if(!this.groupId){
23298             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23299             //return (this.getValue() == this.inputValue) ? true : false;
23300         }
23301         
23302         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23303         
23304         if(!group){
23305             return false;
23306         }
23307         
23308         var r = false;
23309         
23310         for(var i in group){
23311             if(group[i].el.isVisible(true)){
23312                 r = false;
23313                 break;
23314             }
23315             
23316             r = true;
23317         }
23318         
23319         for(var i in group){
23320             if(r){
23321                 break;
23322             }
23323             
23324             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23325         }
23326         
23327         return r;
23328     },
23329     
23330     /**
23331      * Mark this field as valid
23332      */
23333     markValid : function()
23334     {
23335         var _this = this;
23336         
23337         this.fireEvent('valid', this);
23338         
23339         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23340         
23341         if(this.groupId){
23342             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23343         }
23344         
23345         if(label){
23346             label.markValid();
23347         }
23348
23349         if(this.inputType == 'radio'){
23350             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23351                 var fg = e.findParent('.form-group', false, true);
23352                 if (Roo.bootstrap.version == 3) {
23353                     fg.removeClass([_this.invalidClass, _this.validClass]);
23354                     fg.addClass(_this.validClass);
23355                 } else {
23356                     fg.removeClass(['is-valid', 'is-invalid']);
23357                     fg.addClass('is-valid');
23358                 }
23359             });
23360             
23361             return;
23362         }
23363
23364         if(!this.groupId){
23365             var fg = this.el.findParent('.form-group', false, true);
23366             if (Roo.bootstrap.version == 3) {
23367                 fg.removeClass([this.invalidClass, this.validClass]);
23368                 fg.addClass(this.validClass);
23369             } else {
23370                 fg.removeClass(['is-valid', 'is-invalid']);
23371                 fg.addClass('is-valid');
23372             }
23373             return;
23374         }
23375         
23376         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23377         
23378         if(!group){
23379             return;
23380         }
23381         
23382         for(var i in group){
23383             var fg = group[i].el.findParent('.form-group', false, true);
23384             if (Roo.bootstrap.version == 3) {
23385                 fg.removeClass([this.invalidClass, this.validClass]);
23386                 fg.addClass(this.validClass);
23387             } else {
23388                 fg.removeClass(['is-valid', 'is-invalid']);
23389                 fg.addClass('is-valid');
23390             }
23391         }
23392     },
23393     
23394      /**
23395      * Mark this field as invalid
23396      * @param {String} msg The validation message
23397      */
23398     markInvalid : function(msg)
23399     {
23400         if(this.allowBlank){
23401             return;
23402         }
23403         
23404         var _this = this;
23405         
23406         this.fireEvent('invalid', this, msg);
23407         
23408         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23409         
23410         if(this.groupId){
23411             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23412         }
23413         
23414         if(label){
23415             label.markInvalid();
23416         }
23417             
23418         if(this.inputType == 'radio'){
23419             
23420             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23421                 var fg = e.findParent('.form-group', false, true);
23422                 if (Roo.bootstrap.version == 3) {
23423                     fg.removeClass([_this.invalidClass, _this.validClass]);
23424                     fg.addClass(_this.invalidClass);
23425                 } else {
23426                     fg.removeClass(['is-invalid', 'is-valid']);
23427                     fg.addClass('is-invalid');
23428                 }
23429             });
23430             
23431             return;
23432         }
23433         
23434         if(!this.groupId){
23435             var fg = this.el.findParent('.form-group', false, true);
23436             if (Roo.bootstrap.version == 3) {
23437                 fg.removeClass([_this.invalidClass, _this.validClass]);
23438                 fg.addClass(_this.invalidClass);
23439             } else {
23440                 fg.removeClass(['is-invalid', 'is-valid']);
23441                 fg.addClass('is-invalid');
23442             }
23443             return;
23444         }
23445         
23446         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23447         
23448         if(!group){
23449             return;
23450         }
23451         
23452         for(var i in group){
23453             var fg = group[i].el.findParent('.form-group', false, true);
23454             if (Roo.bootstrap.version == 3) {
23455                 fg.removeClass([_this.invalidClass, _this.validClass]);
23456                 fg.addClass(_this.invalidClass);
23457             } else {
23458                 fg.removeClass(['is-invalid', 'is-valid']);
23459                 fg.addClass('is-invalid');
23460             }
23461         }
23462         
23463     },
23464     
23465     clearInvalid : function()
23466     {
23467         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23468         
23469         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23470         
23471         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23472         
23473         if (label && label.iconEl) {
23474             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23475             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23476         }
23477     },
23478     
23479     disable : function()
23480     {
23481         if(this.inputType != 'radio'){
23482             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23483             return;
23484         }
23485         
23486         var _this = this;
23487         
23488         if(this.rendered){
23489             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23490                 _this.getActionEl().addClass(this.disabledClass);
23491                 e.dom.disabled = true;
23492             });
23493         }
23494         
23495         this.disabled = true;
23496         this.fireEvent("disable", this);
23497         return this;
23498     },
23499
23500     enable : function()
23501     {
23502         if(this.inputType != 'radio'){
23503             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23504             return;
23505         }
23506         
23507         var _this = this;
23508         
23509         if(this.rendered){
23510             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23511                 _this.getActionEl().removeClass(this.disabledClass);
23512                 e.dom.disabled = false;
23513             });
23514         }
23515         
23516         this.disabled = false;
23517         this.fireEvent("enable", this);
23518         return this;
23519     },
23520     
23521     setBoxLabel : function(v)
23522     {
23523         this.boxLabel = v;
23524         
23525         if(this.rendered){
23526             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23527         }
23528     }
23529
23530 });
23531
23532 Roo.apply(Roo.bootstrap.CheckBox, {
23533     
23534     groups: {},
23535     
23536      /**
23537     * register a CheckBox Group
23538     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23539     */
23540     register : function(checkbox)
23541     {
23542         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23543             this.groups[checkbox.groupId] = {};
23544         }
23545         
23546         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23547             return;
23548         }
23549         
23550         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23551         
23552     },
23553     /**
23554     * fetch a CheckBox Group based on the group ID
23555     * @param {string} the group ID
23556     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23557     */
23558     get: function(groupId) {
23559         if (typeof(this.groups[groupId]) == 'undefined') {
23560             return false;
23561         }
23562         
23563         return this.groups[groupId] ;
23564     }
23565     
23566     
23567 });
23568 /*
23569  * - LGPL
23570  *
23571  * RadioItem
23572  * 
23573  */
23574
23575 /**
23576  * @class Roo.bootstrap.Radio
23577  * @extends Roo.bootstrap.Component
23578  * Bootstrap Radio class
23579  * @cfg {String} boxLabel - the label associated
23580  * @cfg {String} value - the value of radio
23581  * 
23582  * @constructor
23583  * Create a new Radio
23584  * @param {Object} config The config object
23585  */
23586 Roo.bootstrap.Radio = function(config){
23587     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23588     
23589 };
23590
23591 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23592     
23593     boxLabel : '',
23594     
23595     value : '',
23596     
23597     getAutoCreate : function()
23598     {
23599         var cfg = {
23600             tag : 'div',
23601             cls : 'form-group radio',
23602             cn : [
23603                 {
23604                     tag : 'label',
23605                     cls : 'box-label',
23606                     html : this.boxLabel
23607                 }
23608             ]
23609         };
23610         
23611         return cfg;
23612     },
23613     
23614     initEvents : function() 
23615     {
23616         this.parent().register(this);
23617         
23618         this.el.on('click', this.onClick, this);
23619         
23620     },
23621     
23622     onClick : function(e)
23623     {
23624         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23625             this.setChecked(true);
23626         }
23627     },
23628     
23629     setChecked : function(state, suppressEvent)
23630     {
23631         this.parent().setValue(this.value, suppressEvent);
23632         
23633     },
23634     
23635     setBoxLabel : function(v)
23636     {
23637         this.boxLabel = v;
23638         
23639         if(this.rendered){
23640             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23641         }
23642     }
23643     
23644 });
23645  
23646
23647  /*
23648  * - LGPL
23649  *
23650  * Input
23651  * 
23652  */
23653
23654 /**
23655  * @class Roo.bootstrap.SecurePass
23656  * @extends Roo.bootstrap.Input
23657  * Bootstrap SecurePass class
23658  *
23659  * 
23660  * @constructor
23661  * Create a new SecurePass
23662  * @param {Object} config The config object
23663  */
23664  
23665 Roo.bootstrap.SecurePass = function (config) {
23666     // these go here, so the translation tool can replace them..
23667     this.errors = {
23668         PwdEmpty: "Please type a password, and then retype it to confirm.",
23669         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23670         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23671         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23672         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23673         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23674         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23675         TooWeak: "Your password is Too Weak."
23676     },
23677     this.meterLabel = "Password strength:";
23678     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23679     this.meterClass = [
23680         "roo-password-meter-tooweak", 
23681         "roo-password-meter-weak", 
23682         "roo-password-meter-medium", 
23683         "roo-password-meter-strong", 
23684         "roo-password-meter-grey"
23685     ];
23686     
23687     this.errors = {};
23688     
23689     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23690 }
23691
23692 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23693     /**
23694      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23695      * {
23696      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23697      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23698      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23699      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23700      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23701      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23702      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23703      * })
23704      */
23705     // private
23706     
23707     meterWidth: 300,
23708     errorMsg :'',    
23709     errors: false,
23710     imageRoot: '/',
23711     /**
23712      * @cfg {String/Object} Label for the strength meter (defaults to
23713      * 'Password strength:')
23714      */
23715     // private
23716     meterLabel: '',
23717     /**
23718      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23719      * ['Weak', 'Medium', 'Strong'])
23720      */
23721     // private    
23722     pwdStrengths: false,    
23723     // private
23724     strength: 0,
23725     // private
23726     _lastPwd: null,
23727     // private
23728     kCapitalLetter: 0,
23729     kSmallLetter: 1,
23730     kDigit: 2,
23731     kPunctuation: 3,
23732     
23733     insecure: false,
23734     // private
23735     initEvents: function ()
23736     {
23737         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23738
23739         if (this.el.is('input[type=password]') && Roo.isSafari) {
23740             this.el.on('keydown', this.SafariOnKeyDown, this);
23741         }
23742
23743         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23744     },
23745     // private
23746     onRender: function (ct, position)
23747     {
23748         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23749         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23750         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23751
23752         this.trigger.createChild({
23753                    cn: [
23754                     {
23755                     //id: 'PwdMeter',
23756                     tag: 'div',
23757                     cls: 'roo-password-meter-grey col-xs-12',
23758                     style: {
23759                         //width: 0,
23760                         //width: this.meterWidth + 'px'                                                
23761                         }
23762                     },
23763                     {                            
23764                          cls: 'roo-password-meter-text'                          
23765                     }
23766                 ]            
23767         });
23768
23769          
23770         if (this.hideTrigger) {
23771             this.trigger.setDisplayed(false);
23772         }
23773         this.setSize(this.width || '', this.height || '');
23774     },
23775     // private
23776     onDestroy: function ()
23777     {
23778         if (this.trigger) {
23779             this.trigger.removeAllListeners();
23780             this.trigger.remove();
23781         }
23782         if (this.wrap) {
23783             this.wrap.remove();
23784         }
23785         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23786     },
23787     // private
23788     checkStrength: function ()
23789     {
23790         var pwd = this.inputEl().getValue();
23791         if (pwd == this._lastPwd) {
23792             return;
23793         }
23794
23795         var strength;
23796         if (this.ClientSideStrongPassword(pwd)) {
23797             strength = 3;
23798         } else if (this.ClientSideMediumPassword(pwd)) {
23799             strength = 2;
23800         } else if (this.ClientSideWeakPassword(pwd)) {
23801             strength = 1;
23802         } else {
23803             strength = 0;
23804         }
23805         
23806         Roo.log('strength1: ' + strength);
23807         
23808         //var pm = this.trigger.child('div/div/div').dom;
23809         var pm = this.trigger.child('div/div');
23810         pm.removeClass(this.meterClass);
23811         pm.addClass(this.meterClass[strength]);
23812                 
23813         
23814         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23815                 
23816         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23817         
23818         this._lastPwd = pwd;
23819     },
23820     reset: function ()
23821     {
23822         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23823         
23824         this._lastPwd = '';
23825         
23826         var pm = this.trigger.child('div/div');
23827         pm.removeClass(this.meterClass);
23828         pm.addClass('roo-password-meter-grey');        
23829         
23830         
23831         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23832         
23833         pt.innerHTML = '';
23834         this.inputEl().dom.type='password';
23835     },
23836     // private
23837     validateValue: function (value)
23838     {
23839         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23840             return false;
23841         }
23842         if (value.length == 0) {
23843             if (this.allowBlank) {
23844                 this.clearInvalid();
23845                 return true;
23846             }
23847
23848             this.markInvalid(this.errors.PwdEmpty);
23849             this.errorMsg = this.errors.PwdEmpty;
23850             return false;
23851         }
23852         
23853         if(this.insecure){
23854             return true;
23855         }
23856         
23857         if (!value.match(/[\x21-\x7e]+/)) {
23858             this.markInvalid(this.errors.PwdBadChar);
23859             this.errorMsg = this.errors.PwdBadChar;
23860             return false;
23861         }
23862         if (value.length < 6) {
23863             this.markInvalid(this.errors.PwdShort);
23864             this.errorMsg = this.errors.PwdShort;
23865             return false;
23866         }
23867         if (value.length > 16) {
23868             this.markInvalid(this.errors.PwdLong);
23869             this.errorMsg = this.errors.PwdLong;
23870             return false;
23871         }
23872         var strength;
23873         if (this.ClientSideStrongPassword(value)) {
23874             strength = 3;
23875         } else if (this.ClientSideMediumPassword(value)) {
23876             strength = 2;
23877         } else if (this.ClientSideWeakPassword(value)) {
23878             strength = 1;
23879         } else {
23880             strength = 0;
23881         }
23882
23883         
23884         if (strength < 2) {
23885             //this.markInvalid(this.errors.TooWeak);
23886             this.errorMsg = this.errors.TooWeak;
23887             //return false;
23888         }
23889         
23890         
23891         console.log('strength2: ' + strength);
23892         
23893         //var pm = this.trigger.child('div/div/div').dom;
23894         
23895         var pm = this.trigger.child('div/div');
23896         pm.removeClass(this.meterClass);
23897         pm.addClass(this.meterClass[strength]);
23898                 
23899         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23900                 
23901         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23902         
23903         this.errorMsg = ''; 
23904         return true;
23905     },
23906     // private
23907     CharacterSetChecks: function (type)
23908     {
23909         this.type = type;
23910         this.fResult = false;
23911     },
23912     // private
23913     isctype: function (character, type)
23914     {
23915         switch (type) {  
23916             case this.kCapitalLetter:
23917                 if (character >= 'A' && character <= 'Z') {
23918                     return true;
23919                 }
23920                 break;
23921             
23922             case this.kSmallLetter:
23923                 if (character >= 'a' && character <= 'z') {
23924                     return true;
23925                 }
23926                 break;
23927             
23928             case this.kDigit:
23929                 if (character >= '0' && character <= '9') {
23930                     return true;
23931                 }
23932                 break;
23933             
23934             case this.kPunctuation:
23935                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23936                     return true;
23937                 }
23938                 break;
23939             
23940             default:
23941                 return false;
23942         }
23943
23944     },
23945     // private
23946     IsLongEnough: function (pwd, size)
23947     {
23948         return !(pwd == null || isNaN(size) || pwd.length < size);
23949     },
23950     // private
23951     SpansEnoughCharacterSets: function (word, nb)
23952     {
23953         if (!this.IsLongEnough(word, nb))
23954         {
23955             return false;
23956         }
23957
23958         var characterSetChecks = new Array(
23959             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23960             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23961         );
23962         
23963         for (var index = 0; index < word.length; ++index) {
23964             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23965                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23966                     characterSetChecks[nCharSet].fResult = true;
23967                     break;
23968                 }
23969             }
23970         }
23971
23972         var nCharSets = 0;
23973         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23974             if (characterSetChecks[nCharSet].fResult) {
23975                 ++nCharSets;
23976             }
23977         }
23978
23979         if (nCharSets < nb) {
23980             return false;
23981         }
23982         return true;
23983     },
23984     // private
23985     ClientSideStrongPassword: function (pwd)
23986     {
23987         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23988     },
23989     // private
23990     ClientSideMediumPassword: function (pwd)
23991     {
23992         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23993     },
23994     // private
23995     ClientSideWeakPassword: function (pwd)
23996     {
23997         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23998     }
23999           
24000 })//<script type="text/javascript">
24001
24002 /*
24003  * Based  Ext JS Library 1.1.1
24004  * Copyright(c) 2006-2007, Ext JS, LLC.
24005  * LGPL
24006  *
24007  */
24008  
24009 /**
24010  * @class Roo.HtmlEditorCore
24011  * @extends Roo.Component
24012  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24013  *
24014  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24015  */
24016
24017 Roo.HtmlEditorCore = function(config){
24018     
24019     
24020     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24021     
24022     
24023     this.addEvents({
24024         /**
24025          * @event initialize
24026          * Fires when the editor is fully initialized (including the iframe)
24027          * @param {Roo.HtmlEditorCore} this
24028          */
24029         initialize: true,
24030         /**
24031          * @event activate
24032          * Fires when the editor is first receives the focus. Any insertion must wait
24033          * until after this event.
24034          * @param {Roo.HtmlEditorCore} this
24035          */
24036         activate: true,
24037          /**
24038          * @event beforesync
24039          * Fires before the textarea is updated with content from the editor iframe. Return false
24040          * to cancel the sync.
24041          * @param {Roo.HtmlEditorCore} this
24042          * @param {String} html
24043          */
24044         beforesync: true,
24045          /**
24046          * @event beforepush
24047          * Fires before the iframe editor is updated with content from the textarea. Return false
24048          * to cancel the push.
24049          * @param {Roo.HtmlEditorCore} this
24050          * @param {String} html
24051          */
24052         beforepush: true,
24053          /**
24054          * @event sync
24055          * Fires when the textarea is updated with content from the editor iframe.
24056          * @param {Roo.HtmlEditorCore} this
24057          * @param {String} html
24058          */
24059         sync: true,
24060          /**
24061          * @event push
24062          * Fires when the iframe editor is updated with content from the textarea.
24063          * @param {Roo.HtmlEditorCore} this
24064          * @param {String} html
24065          */
24066         push: true,
24067         
24068         /**
24069          * @event editorevent
24070          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24071          * @param {Roo.HtmlEditorCore} this
24072          */
24073         editorevent: true
24074         
24075     });
24076     
24077     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24078     
24079     // defaults : white / black...
24080     this.applyBlacklists();
24081     
24082     
24083     
24084 };
24085
24086
24087 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24088
24089
24090      /**
24091      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24092      */
24093     
24094     owner : false,
24095     
24096      /**
24097      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24098      *                        Roo.resizable.
24099      */
24100     resizable : false,
24101      /**
24102      * @cfg {Number} height (in pixels)
24103      */   
24104     height: 300,
24105    /**
24106      * @cfg {Number} width (in pixels)
24107      */   
24108     width: 500,
24109     
24110     /**
24111      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24112      * 
24113      */
24114     stylesheets: false,
24115     
24116     // id of frame..
24117     frameId: false,
24118     
24119     // private properties
24120     validationEvent : false,
24121     deferHeight: true,
24122     initialized : false,
24123     activated : false,
24124     sourceEditMode : false,
24125     onFocus : Roo.emptyFn,
24126     iframePad:3,
24127     hideMode:'offsets',
24128     
24129     clearUp: true,
24130     
24131     // blacklist + whitelisted elements..
24132     black: false,
24133     white: false,
24134      
24135     bodyCls : '',
24136
24137     /**
24138      * Protected method that will not generally be called directly. It
24139      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24140      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24141      */
24142     getDocMarkup : function(){
24143         // body styles..
24144         var st = '';
24145         
24146         // inherit styels from page...?? 
24147         if (this.stylesheets === false) {
24148             
24149             Roo.get(document.head).select('style').each(function(node) {
24150                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24151             });
24152             
24153             Roo.get(document.head).select('link').each(function(node) { 
24154                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24155             });
24156             
24157         } else if (!this.stylesheets.length) {
24158                 // simple..
24159                 st = '<style type="text/css">' +
24160                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24161                    '</style>';
24162         } else {
24163             for (var i in this.stylesheets) { 
24164                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24165             }
24166             
24167         }
24168         
24169         st +=  '<style type="text/css">' +
24170             'IMG { cursor: pointer } ' +
24171         '</style>';
24172
24173         var cls = 'roo-htmleditor-body';
24174         
24175         if(this.bodyCls.length){
24176             cls += ' ' + this.bodyCls;
24177         }
24178         
24179         return '<html><head>' + st  +
24180             //<style type="text/css">' +
24181             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24182             //'</style>' +
24183             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24184     },
24185
24186     // private
24187     onRender : function(ct, position)
24188     {
24189         var _t = this;
24190         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24191         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24192         
24193         
24194         this.el.dom.style.border = '0 none';
24195         this.el.dom.setAttribute('tabIndex', -1);
24196         this.el.addClass('x-hidden hide');
24197         
24198         
24199         
24200         if(Roo.isIE){ // fix IE 1px bogus margin
24201             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24202         }
24203        
24204         
24205         this.frameId = Roo.id();
24206         
24207          
24208         
24209         var iframe = this.owner.wrap.createChild({
24210             tag: 'iframe',
24211             cls: 'form-control', // bootstrap..
24212             id: this.frameId,
24213             name: this.frameId,
24214             frameBorder : 'no',
24215             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24216         }, this.el
24217         );
24218         
24219         
24220         this.iframe = iframe.dom;
24221
24222          this.assignDocWin();
24223         
24224         this.doc.designMode = 'on';
24225        
24226         this.doc.open();
24227         this.doc.write(this.getDocMarkup());
24228         this.doc.close();
24229
24230         
24231         var task = { // must defer to wait for browser to be ready
24232             run : function(){
24233                 //console.log("run task?" + this.doc.readyState);
24234                 this.assignDocWin();
24235                 if(this.doc.body || this.doc.readyState == 'complete'){
24236                     try {
24237                         this.doc.designMode="on";
24238                     } catch (e) {
24239                         return;
24240                     }
24241                     Roo.TaskMgr.stop(task);
24242                     this.initEditor.defer(10, this);
24243                 }
24244             },
24245             interval : 10,
24246             duration: 10000,
24247             scope: this
24248         };
24249         Roo.TaskMgr.start(task);
24250
24251     },
24252
24253     // private
24254     onResize : function(w, h)
24255     {
24256          Roo.log('resize: ' +w + ',' + h );
24257         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24258         if(!this.iframe){
24259             return;
24260         }
24261         if(typeof w == 'number'){
24262             
24263             this.iframe.style.width = w + 'px';
24264         }
24265         if(typeof h == 'number'){
24266             
24267             this.iframe.style.height = h + 'px';
24268             if(this.doc){
24269                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24270             }
24271         }
24272         
24273     },
24274
24275     /**
24276      * Toggles the editor between standard and source edit mode.
24277      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24278      */
24279     toggleSourceEdit : function(sourceEditMode){
24280         
24281         this.sourceEditMode = sourceEditMode === true;
24282         
24283         if(this.sourceEditMode){
24284  
24285             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24286             
24287         }else{
24288             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24289             //this.iframe.className = '';
24290             this.deferFocus();
24291         }
24292         //this.setSize(this.owner.wrap.getSize());
24293         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24294     },
24295
24296     
24297   
24298
24299     /**
24300      * Protected method that will not generally be called directly. If you need/want
24301      * custom HTML cleanup, this is the method you should override.
24302      * @param {String} html The HTML to be cleaned
24303      * return {String} The cleaned HTML
24304      */
24305     cleanHtml : function(html){
24306         html = String(html);
24307         if(html.length > 5){
24308             if(Roo.isSafari){ // strip safari nonsense
24309                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24310             }
24311         }
24312         if(html == '&nbsp;'){
24313             html = '';
24314         }
24315         return html;
24316     },
24317
24318     /**
24319      * HTML Editor -> Textarea
24320      * Protected method that will not generally be called directly. Syncs the contents
24321      * of the editor iframe with the textarea.
24322      */
24323     syncValue : function(){
24324         if(this.initialized){
24325             var bd = (this.doc.body || this.doc.documentElement);
24326             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24327             var html = bd.innerHTML;
24328             if(Roo.isSafari){
24329                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24330                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24331                 if(m && m[1]){
24332                     html = '<div style="'+m[0]+'">' + html + '</div>';
24333                 }
24334             }
24335             html = this.cleanHtml(html);
24336             // fix up the special chars.. normaly like back quotes in word...
24337             // however we do not want to do this with chinese..
24338             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24339                 
24340                 var cc = match.charCodeAt();
24341
24342                 // Get the character value, handling surrogate pairs
24343                 if (match.length == 2) {
24344                     // It's a surrogate pair, calculate the Unicode code point
24345                     var high = match.charCodeAt(0) - 0xD800;
24346                     var low  = match.charCodeAt(1) - 0xDC00;
24347                     cc = (high * 0x400) + low + 0x10000;
24348                 }  else if (
24349                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24350                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24351                     (cc >= 0xf900 && cc < 0xfb00 )
24352                 ) {
24353                         return match;
24354                 }  
24355          
24356                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24357                 return "&#" + cc + ";";
24358                 
24359                 
24360             });
24361             
24362             
24363              
24364             if(this.owner.fireEvent('beforesync', this, html) !== false){
24365                 this.el.dom.value = html;
24366                 this.owner.fireEvent('sync', this, html);
24367             }
24368         }
24369     },
24370
24371     /**
24372      * Protected method that will not generally be called directly. Pushes the value of the textarea
24373      * into the iframe editor.
24374      */
24375     pushValue : function(){
24376         if(this.initialized){
24377             var v = this.el.dom.value.trim();
24378             
24379 //            if(v.length < 1){
24380 //                v = '&#160;';
24381 //            }
24382             
24383             if(this.owner.fireEvent('beforepush', this, v) !== false){
24384                 var d = (this.doc.body || this.doc.documentElement);
24385                 d.innerHTML = v;
24386                 this.cleanUpPaste();
24387                 this.el.dom.value = d.innerHTML;
24388                 this.owner.fireEvent('push', this, v);
24389             }
24390         }
24391     },
24392
24393     // private
24394     deferFocus : function(){
24395         this.focus.defer(10, this);
24396     },
24397
24398     // doc'ed in Field
24399     focus : function(){
24400         if(this.win && !this.sourceEditMode){
24401             this.win.focus();
24402         }else{
24403             this.el.focus();
24404         }
24405     },
24406     
24407     assignDocWin: function()
24408     {
24409         var iframe = this.iframe;
24410         
24411          if(Roo.isIE){
24412             this.doc = iframe.contentWindow.document;
24413             this.win = iframe.contentWindow;
24414         } else {
24415 //            if (!Roo.get(this.frameId)) {
24416 //                return;
24417 //            }
24418 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24419 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24420             
24421             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24422                 return;
24423             }
24424             
24425             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24426             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24427         }
24428     },
24429     
24430     // private
24431     initEditor : function(){
24432         //console.log("INIT EDITOR");
24433         this.assignDocWin();
24434         
24435         
24436         
24437         this.doc.designMode="on";
24438         this.doc.open();
24439         this.doc.write(this.getDocMarkup());
24440         this.doc.close();
24441         
24442         var dbody = (this.doc.body || this.doc.documentElement);
24443         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24444         // this copies styles from the containing element into thsi one..
24445         // not sure why we need all of this..
24446         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24447         
24448         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24449         //ss['background-attachment'] = 'fixed'; // w3c
24450         dbody.bgProperties = 'fixed'; // ie
24451         //Roo.DomHelper.applyStyles(dbody, ss);
24452         Roo.EventManager.on(this.doc, {
24453             //'mousedown': this.onEditorEvent,
24454             'mouseup': this.onEditorEvent,
24455             'dblclick': this.onEditorEvent,
24456             'click': this.onEditorEvent,
24457             'keyup': this.onEditorEvent,
24458             buffer:100,
24459             scope: this
24460         });
24461         if(Roo.isGecko){
24462             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24463         }
24464         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24465             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24466         }
24467         this.initialized = true;
24468
24469         this.owner.fireEvent('initialize', this);
24470         this.pushValue();
24471     },
24472
24473     // private
24474     onDestroy : function(){
24475         
24476         
24477         
24478         if(this.rendered){
24479             
24480             //for (var i =0; i < this.toolbars.length;i++) {
24481             //    // fixme - ask toolbars for heights?
24482             //    this.toolbars[i].onDestroy();
24483            // }
24484             
24485             //this.wrap.dom.innerHTML = '';
24486             //this.wrap.remove();
24487         }
24488     },
24489
24490     // private
24491     onFirstFocus : function(){
24492         
24493         this.assignDocWin();
24494         
24495         
24496         this.activated = true;
24497          
24498     
24499         if(Roo.isGecko){ // prevent silly gecko errors
24500             this.win.focus();
24501             var s = this.win.getSelection();
24502             if(!s.focusNode || s.focusNode.nodeType != 3){
24503                 var r = s.getRangeAt(0);
24504                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24505                 r.collapse(true);
24506                 this.deferFocus();
24507             }
24508             try{
24509                 this.execCmd('useCSS', true);
24510                 this.execCmd('styleWithCSS', false);
24511             }catch(e){}
24512         }
24513         this.owner.fireEvent('activate', this);
24514     },
24515
24516     // private
24517     adjustFont: function(btn){
24518         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24519         //if(Roo.isSafari){ // safari
24520         //    adjust *= 2;
24521        // }
24522         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24523         if(Roo.isSafari){ // safari
24524             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24525             v =  (v < 10) ? 10 : v;
24526             v =  (v > 48) ? 48 : v;
24527             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24528             
24529         }
24530         
24531         
24532         v = Math.max(1, v+adjust);
24533         
24534         this.execCmd('FontSize', v  );
24535     },
24536
24537     onEditorEvent : function(e)
24538     {
24539         this.owner.fireEvent('editorevent', this, e);
24540       //  this.updateToolbar();
24541         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24542     },
24543
24544     insertTag : function(tg)
24545     {
24546         // could be a bit smarter... -> wrap the current selected tRoo..
24547         if (tg.toLowerCase() == 'span' ||
24548             tg.toLowerCase() == 'code' ||
24549             tg.toLowerCase() == 'sup' ||
24550             tg.toLowerCase() == 'sub' 
24551             ) {
24552             
24553             range = this.createRange(this.getSelection());
24554             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24555             wrappingNode.appendChild(range.extractContents());
24556             range.insertNode(wrappingNode);
24557
24558             return;
24559             
24560             
24561             
24562         }
24563         this.execCmd("formatblock",   tg);
24564         
24565     },
24566     
24567     insertText : function(txt)
24568     {
24569         
24570         
24571         var range = this.createRange();
24572         range.deleteContents();
24573                //alert(Sender.getAttribute('label'));
24574                
24575         range.insertNode(this.doc.createTextNode(txt));
24576     } ,
24577     
24578      
24579
24580     /**
24581      * Executes a Midas editor command on the editor document and performs necessary focus and
24582      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24583      * @param {String} cmd The Midas command
24584      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24585      */
24586     relayCmd : function(cmd, value){
24587         this.win.focus();
24588         this.execCmd(cmd, value);
24589         this.owner.fireEvent('editorevent', this);
24590         //this.updateToolbar();
24591         this.owner.deferFocus();
24592     },
24593
24594     /**
24595      * Executes a Midas editor command directly on the editor document.
24596      * For visual commands, you should use {@link #relayCmd} instead.
24597      * <b>This should only be called after the editor is initialized.</b>
24598      * @param {String} cmd The Midas command
24599      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24600      */
24601     execCmd : function(cmd, value){
24602         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24603         this.syncValue();
24604     },
24605  
24606  
24607    
24608     /**
24609      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24610      * to insert tRoo.
24611      * @param {String} text | dom node.. 
24612      */
24613     insertAtCursor : function(text)
24614     {
24615         
24616         if(!this.activated){
24617             return;
24618         }
24619         /*
24620         if(Roo.isIE){
24621             this.win.focus();
24622             var r = this.doc.selection.createRange();
24623             if(r){
24624                 r.collapse(true);
24625                 r.pasteHTML(text);
24626                 this.syncValue();
24627                 this.deferFocus();
24628             
24629             }
24630             return;
24631         }
24632         */
24633         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24634             this.win.focus();
24635             
24636             
24637             // from jquery ui (MIT licenced)
24638             var range, node;
24639             var win = this.win;
24640             
24641             if (win.getSelection && win.getSelection().getRangeAt) {
24642                 range = win.getSelection().getRangeAt(0);
24643                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24644                 range.insertNode(node);
24645             } else if (win.document.selection && win.document.selection.createRange) {
24646                 // no firefox support
24647                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24648                 win.document.selection.createRange().pasteHTML(txt);
24649             } else {
24650                 // no firefox support
24651                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24652                 this.execCmd('InsertHTML', txt);
24653             } 
24654             
24655             this.syncValue();
24656             
24657             this.deferFocus();
24658         }
24659     },
24660  // private
24661     mozKeyPress : function(e){
24662         if(e.ctrlKey){
24663             var c = e.getCharCode(), cmd;
24664           
24665             if(c > 0){
24666                 c = String.fromCharCode(c).toLowerCase();
24667                 switch(c){
24668                     case 'b':
24669                         cmd = 'bold';
24670                         break;
24671                     case 'i':
24672                         cmd = 'italic';
24673                         break;
24674                     
24675                     case 'u':
24676                         cmd = 'underline';
24677                         break;
24678                     
24679                     case 'v':
24680                         this.cleanUpPaste.defer(100, this);
24681                         return;
24682                         
24683                 }
24684                 if(cmd){
24685                     this.win.focus();
24686                     this.execCmd(cmd);
24687                     this.deferFocus();
24688                     e.preventDefault();
24689                 }
24690                 
24691             }
24692         }
24693     },
24694
24695     // private
24696     fixKeys : function(){ // load time branching for fastest keydown performance
24697         if(Roo.isIE){
24698             return function(e){
24699                 var k = e.getKey(), r;
24700                 if(k == e.TAB){
24701                     e.stopEvent();
24702                     r = this.doc.selection.createRange();
24703                     if(r){
24704                         r.collapse(true);
24705                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24706                         this.deferFocus();
24707                     }
24708                     return;
24709                 }
24710                 
24711                 if(k == e.ENTER){
24712                     r = this.doc.selection.createRange();
24713                     if(r){
24714                         var target = r.parentElement();
24715                         if(!target || target.tagName.toLowerCase() != 'li'){
24716                             e.stopEvent();
24717                             r.pasteHTML('<br />');
24718                             r.collapse(false);
24719                             r.select();
24720                         }
24721                     }
24722                 }
24723                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24724                     this.cleanUpPaste.defer(100, this);
24725                     return;
24726                 }
24727                 
24728                 
24729             };
24730         }else if(Roo.isOpera){
24731             return function(e){
24732                 var k = e.getKey();
24733                 if(k == e.TAB){
24734                     e.stopEvent();
24735                     this.win.focus();
24736                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24737                     this.deferFocus();
24738                 }
24739                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24740                     this.cleanUpPaste.defer(100, this);
24741                     return;
24742                 }
24743                 
24744             };
24745         }else if(Roo.isSafari){
24746             return function(e){
24747                 var k = e.getKey();
24748                 
24749                 if(k == e.TAB){
24750                     e.stopEvent();
24751                     this.execCmd('InsertText','\t');
24752                     this.deferFocus();
24753                     return;
24754                 }
24755                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24756                     this.cleanUpPaste.defer(100, this);
24757                     return;
24758                 }
24759                 
24760              };
24761         }
24762     }(),
24763     
24764     getAllAncestors: function()
24765     {
24766         var p = this.getSelectedNode();
24767         var a = [];
24768         if (!p) {
24769             a.push(p); // push blank onto stack..
24770             p = this.getParentElement();
24771         }
24772         
24773         
24774         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24775             a.push(p);
24776             p = p.parentNode;
24777         }
24778         a.push(this.doc.body);
24779         return a;
24780     },
24781     lastSel : false,
24782     lastSelNode : false,
24783     
24784     
24785     getSelection : function() 
24786     {
24787         this.assignDocWin();
24788         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24789     },
24790     
24791     getSelectedNode: function() 
24792     {
24793         // this may only work on Gecko!!!
24794         
24795         // should we cache this!!!!
24796         
24797         
24798         
24799          
24800         var range = this.createRange(this.getSelection()).cloneRange();
24801         
24802         if (Roo.isIE) {
24803             var parent = range.parentElement();
24804             while (true) {
24805                 var testRange = range.duplicate();
24806                 testRange.moveToElementText(parent);
24807                 if (testRange.inRange(range)) {
24808                     break;
24809                 }
24810                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24811                     break;
24812                 }
24813                 parent = parent.parentElement;
24814             }
24815             return parent;
24816         }
24817         
24818         // is ancestor a text element.
24819         var ac =  range.commonAncestorContainer;
24820         if (ac.nodeType == 3) {
24821             ac = ac.parentNode;
24822         }
24823         
24824         var ar = ac.childNodes;
24825          
24826         var nodes = [];
24827         var other_nodes = [];
24828         var has_other_nodes = false;
24829         for (var i=0;i<ar.length;i++) {
24830             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24831                 continue;
24832             }
24833             // fullly contained node.
24834             
24835             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24836                 nodes.push(ar[i]);
24837                 continue;
24838             }
24839             
24840             // probably selected..
24841             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24842                 other_nodes.push(ar[i]);
24843                 continue;
24844             }
24845             // outer..
24846             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24847                 continue;
24848             }
24849             
24850             
24851             has_other_nodes = true;
24852         }
24853         if (!nodes.length && other_nodes.length) {
24854             nodes= other_nodes;
24855         }
24856         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24857             return false;
24858         }
24859         
24860         return nodes[0];
24861     },
24862     createRange: function(sel)
24863     {
24864         // this has strange effects when using with 
24865         // top toolbar - not sure if it's a great idea.
24866         //this.editor.contentWindow.focus();
24867         if (typeof sel != "undefined") {
24868             try {
24869                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24870             } catch(e) {
24871                 return this.doc.createRange();
24872             }
24873         } else {
24874             return this.doc.createRange();
24875         }
24876     },
24877     getParentElement: function()
24878     {
24879         
24880         this.assignDocWin();
24881         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24882         
24883         var range = this.createRange(sel);
24884          
24885         try {
24886             var p = range.commonAncestorContainer;
24887             while (p.nodeType == 3) { // text node
24888                 p = p.parentNode;
24889             }
24890             return p;
24891         } catch (e) {
24892             return null;
24893         }
24894     
24895     },
24896     /***
24897      *
24898      * Range intersection.. the hard stuff...
24899      *  '-1' = before
24900      *  '0' = hits..
24901      *  '1' = after.
24902      *         [ -- selected range --- ]
24903      *   [fail]                        [fail]
24904      *
24905      *    basically..
24906      *      if end is before start or  hits it. fail.
24907      *      if start is after end or hits it fail.
24908      *
24909      *   if either hits (but other is outside. - then it's not 
24910      *   
24911      *    
24912      **/
24913     
24914     
24915     // @see http://www.thismuchiknow.co.uk/?p=64.
24916     rangeIntersectsNode : function(range, node)
24917     {
24918         var nodeRange = node.ownerDocument.createRange();
24919         try {
24920             nodeRange.selectNode(node);
24921         } catch (e) {
24922             nodeRange.selectNodeContents(node);
24923         }
24924     
24925         var rangeStartRange = range.cloneRange();
24926         rangeStartRange.collapse(true);
24927     
24928         var rangeEndRange = range.cloneRange();
24929         rangeEndRange.collapse(false);
24930     
24931         var nodeStartRange = nodeRange.cloneRange();
24932         nodeStartRange.collapse(true);
24933     
24934         var nodeEndRange = nodeRange.cloneRange();
24935         nodeEndRange.collapse(false);
24936     
24937         return rangeStartRange.compareBoundaryPoints(
24938                  Range.START_TO_START, nodeEndRange) == -1 &&
24939                rangeEndRange.compareBoundaryPoints(
24940                  Range.START_TO_START, nodeStartRange) == 1;
24941         
24942          
24943     },
24944     rangeCompareNode : function(range, node)
24945     {
24946         var nodeRange = node.ownerDocument.createRange();
24947         try {
24948             nodeRange.selectNode(node);
24949         } catch (e) {
24950             nodeRange.selectNodeContents(node);
24951         }
24952         
24953         
24954         range.collapse(true);
24955     
24956         nodeRange.collapse(true);
24957      
24958         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24959         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24960          
24961         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24962         
24963         var nodeIsBefore   =  ss == 1;
24964         var nodeIsAfter    = ee == -1;
24965         
24966         if (nodeIsBefore && nodeIsAfter) {
24967             return 0; // outer
24968         }
24969         if (!nodeIsBefore && nodeIsAfter) {
24970             return 1; //right trailed.
24971         }
24972         
24973         if (nodeIsBefore && !nodeIsAfter) {
24974             return 2;  // left trailed.
24975         }
24976         // fully contined.
24977         return 3;
24978     },
24979
24980     // private? - in a new class?
24981     cleanUpPaste :  function()
24982     {
24983         // cleans up the whole document..
24984         Roo.log('cleanuppaste');
24985         
24986         this.cleanUpChildren(this.doc.body);
24987         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24988         if (clean != this.doc.body.innerHTML) {
24989             this.doc.body.innerHTML = clean;
24990         }
24991         
24992     },
24993     
24994     cleanWordChars : function(input) {// change the chars to hex code
24995         var he = Roo.HtmlEditorCore;
24996         
24997         var output = input;
24998         Roo.each(he.swapCodes, function(sw) { 
24999             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25000             
25001             output = output.replace(swapper, sw[1]);
25002         });
25003         
25004         return output;
25005     },
25006     
25007     
25008     cleanUpChildren : function (n)
25009     {
25010         if (!n.childNodes.length) {
25011             return;
25012         }
25013         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25014            this.cleanUpChild(n.childNodes[i]);
25015         }
25016     },
25017     
25018     
25019         
25020     
25021     cleanUpChild : function (node)
25022     {
25023         var ed = this;
25024         //console.log(node);
25025         if (node.nodeName == "#text") {
25026             // clean up silly Windows -- stuff?
25027             return; 
25028         }
25029         if (node.nodeName == "#comment") {
25030             node.parentNode.removeChild(node);
25031             // clean up silly Windows -- stuff?
25032             return; 
25033         }
25034         var lcname = node.tagName.toLowerCase();
25035         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25036         // whitelist of tags..
25037         
25038         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25039             // remove node.
25040             node.parentNode.removeChild(node);
25041             return;
25042             
25043         }
25044         
25045         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25046         
25047         // spans with no attributes - just remove them..
25048         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25049             remove_keep_children = true;
25050         }
25051         
25052         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25053         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25054         
25055         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25056         //    remove_keep_children = true;
25057         //}
25058         
25059         if (remove_keep_children) {
25060             this.cleanUpChildren(node);
25061             // inserts everything just before this node...
25062             while (node.childNodes.length) {
25063                 var cn = node.childNodes[0];
25064                 node.removeChild(cn);
25065                 node.parentNode.insertBefore(cn, node);
25066             }
25067             node.parentNode.removeChild(node);
25068             return;
25069         }
25070         
25071         if (!node.attributes || !node.attributes.length) {
25072             
25073           
25074             
25075             
25076             this.cleanUpChildren(node);
25077             return;
25078         }
25079         
25080         function cleanAttr(n,v)
25081         {
25082             
25083             if (v.match(/^\./) || v.match(/^\//)) {
25084                 return;
25085             }
25086             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25087                 return;
25088             }
25089             if (v.match(/^#/)) {
25090                 return;
25091             }
25092             if (v.match(/^\{/)) { // allow template editing.
25093                 return;
25094             }
25095 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25096             node.removeAttribute(n);
25097             
25098         }
25099         
25100         var cwhite = this.cwhite;
25101         var cblack = this.cblack;
25102             
25103         function cleanStyle(n,v)
25104         {
25105             if (v.match(/expression/)) { //XSS?? should we even bother..
25106                 node.removeAttribute(n);
25107                 return;
25108             }
25109             
25110             var parts = v.split(/;/);
25111             var clean = [];
25112             
25113             Roo.each(parts, function(p) {
25114                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25115                 if (!p.length) {
25116                     return true;
25117                 }
25118                 var l = p.split(':').shift().replace(/\s+/g,'');
25119                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25120                 
25121                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25122 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25123                     //node.removeAttribute(n);
25124                     return true;
25125                 }
25126                 //Roo.log()
25127                 // only allow 'c whitelisted system attributes'
25128                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25129 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25130                     //node.removeAttribute(n);
25131                     return true;
25132                 }
25133                 
25134                 
25135                  
25136                 
25137                 clean.push(p);
25138                 return true;
25139             });
25140             if (clean.length) { 
25141                 node.setAttribute(n, clean.join(';'));
25142             } else {
25143                 node.removeAttribute(n);
25144             }
25145             
25146         }
25147         
25148         
25149         for (var i = node.attributes.length-1; i > -1 ; i--) {
25150             var a = node.attributes[i];
25151             //console.log(a);
25152             
25153             if (a.name.toLowerCase().substr(0,2)=='on')  {
25154                 node.removeAttribute(a.name);
25155                 continue;
25156             }
25157             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25158                 node.removeAttribute(a.name);
25159                 continue;
25160             }
25161             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25162                 cleanAttr(a.name,a.value); // fixme..
25163                 continue;
25164             }
25165             if (a.name == 'style') {
25166                 cleanStyle(a.name,a.value);
25167                 continue;
25168             }
25169             /// clean up MS crap..
25170             // tecnically this should be a list of valid class'es..
25171             
25172             
25173             if (a.name == 'class') {
25174                 if (a.value.match(/^Mso/)) {
25175                     node.removeAttribute('class');
25176                 }
25177                 
25178                 if (a.value.match(/^body$/)) {
25179                     node.removeAttribute('class');
25180                 }
25181                 continue;
25182             }
25183             
25184             // style cleanup!?
25185             // class cleanup?
25186             
25187         }
25188         
25189         
25190         this.cleanUpChildren(node);
25191         
25192         
25193     },
25194     
25195     /**
25196      * Clean up MS wordisms...
25197      */
25198     cleanWord : function(node)
25199     {
25200         if (!node) {
25201             this.cleanWord(this.doc.body);
25202             return;
25203         }
25204         
25205         if(
25206                 node.nodeName == 'SPAN' &&
25207                 !node.hasAttributes() &&
25208                 node.childNodes.length == 1 &&
25209                 node.firstChild.nodeName == "#text"  
25210         ) {
25211             var textNode = node.firstChild;
25212             node.removeChild(textNode);
25213             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25214                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25215             }
25216             node.parentNode.insertBefore(textNode, node);
25217             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25218                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25219             }
25220             node.parentNode.removeChild(node);
25221         }
25222         
25223         if (node.nodeName == "#text") {
25224             // clean up silly Windows -- stuff?
25225             return; 
25226         }
25227         if (node.nodeName == "#comment") {
25228             node.parentNode.removeChild(node);
25229             // clean up silly Windows -- stuff?
25230             return; 
25231         }
25232         
25233         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25234             node.parentNode.removeChild(node);
25235             return;
25236         }
25237         //Roo.log(node.tagName);
25238         // remove - but keep children..
25239         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25240             //Roo.log('-- removed');
25241             while (node.childNodes.length) {
25242                 var cn = node.childNodes[0];
25243                 node.removeChild(cn);
25244                 node.parentNode.insertBefore(cn, node);
25245                 // move node to parent - and clean it..
25246                 this.cleanWord(cn);
25247             }
25248             node.parentNode.removeChild(node);
25249             /// no need to iterate chidlren = it's got none..
25250             //this.iterateChildren(node, this.cleanWord);
25251             return;
25252         }
25253         // clean styles
25254         if (node.className.length) {
25255             
25256             var cn = node.className.split(/\W+/);
25257             var cna = [];
25258             Roo.each(cn, function(cls) {
25259                 if (cls.match(/Mso[a-zA-Z]+/)) {
25260                     return;
25261                 }
25262                 cna.push(cls);
25263             });
25264             node.className = cna.length ? cna.join(' ') : '';
25265             if (!cna.length) {
25266                 node.removeAttribute("class");
25267             }
25268         }
25269         
25270         if (node.hasAttribute("lang")) {
25271             node.removeAttribute("lang");
25272         }
25273         
25274         if (node.hasAttribute("style")) {
25275             
25276             var styles = node.getAttribute("style").split(";");
25277             var nstyle = [];
25278             Roo.each(styles, function(s) {
25279                 if (!s.match(/:/)) {
25280                     return;
25281                 }
25282                 var kv = s.split(":");
25283                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25284                     return;
25285                 }
25286                 // what ever is left... we allow.
25287                 nstyle.push(s);
25288             });
25289             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25290             if (!nstyle.length) {
25291                 node.removeAttribute('style');
25292             }
25293         }
25294         this.iterateChildren(node, this.cleanWord);
25295         
25296         
25297         
25298     },
25299     /**
25300      * iterateChildren of a Node, calling fn each time, using this as the scole..
25301      * @param {DomNode} node node to iterate children of.
25302      * @param {Function} fn method of this class to call on each item.
25303      */
25304     iterateChildren : function(node, fn)
25305     {
25306         if (!node.childNodes.length) {
25307                 return;
25308         }
25309         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25310            fn.call(this, node.childNodes[i])
25311         }
25312     },
25313     
25314     
25315     /**
25316      * cleanTableWidths.
25317      *
25318      * Quite often pasting from word etc.. results in tables with column and widths.
25319      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25320      *
25321      */
25322     cleanTableWidths : function(node)
25323     {
25324          
25325          
25326         if (!node) {
25327             this.cleanTableWidths(this.doc.body);
25328             return;
25329         }
25330         
25331         // ignore list...
25332         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25333             return; 
25334         }
25335         Roo.log(node.tagName);
25336         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25337             this.iterateChildren(node, this.cleanTableWidths);
25338             return;
25339         }
25340         if (node.hasAttribute('width')) {
25341             node.removeAttribute('width');
25342         }
25343         
25344          
25345         if (node.hasAttribute("style")) {
25346             // pretty basic...
25347             
25348             var styles = node.getAttribute("style").split(";");
25349             var nstyle = [];
25350             Roo.each(styles, function(s) {
25351                 if (!s.match(/:/)) {
25352                     return;
25353                 }
25354                 var kv = s.split(":");
25355                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25356                     return;
25357                 }
25358                 // what ever is left... we allow.
25359                 nstyle.push(s);
25360             });
25361             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25362             if (!nstyle.length) {
25363                 node.removeAttribute('style');
25364             }
25365         }
25366         
25367         this.iterateChildren(node, this.cleanTableWidths);
25368         
25369         
25370     },
25371     
25372     
25373     
25374     
25375     domToHTML : function(currentElement, depth, nopadtext) {
25376         
25377         depth = depth || 0;
25378         nopadtext = nopadtext || false;
25379     
25380         if (!currentElement) {
25381             return this.domToHTML(this.doc.body);
25382         }
25383         
25384         //Roo.log(currentElement);
25385         var j;
25386         var allText = false;
25387         var nodeName = currentElement.nodeName;
25388         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25389         
25390         if  (nodeName == '#text') {
25391             
25392             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25393         }
25394         
25395         
25396         var ret = '';
25397         if (nodeName != 'BODY') {
25398              
25399             var i = 0;
25400             // Prints the node tagName, such as <A>, <IMG>, etc
25401             if (tagName) {
25402                 var attr = [];
25403                 for(i = 0; i < currentElement.attributes.length;i++) {
25404                     // quoting?
25405                     var aname = currentElement.attributes.item(i).name;
25406                     if (!currentElement.attributes.item(i).value.length) {
25407                         continue;
25408                     }
25409                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25410                 }
25411                 
25412                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25413             } 
25414             else {
25415                 
25416                 // eack
25417             }
25418         } else {
25419             tagName = false;
25420         }
25421         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25422             return ret;
25423         }
25424         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25425             nopadtext = true;
25426         }
25427         
25428         
25429         // Traverse the tree
25430         i = 0;
25431         var currentElementChild = currentElement.childNodes.item(i);
25432         var allText = true;
25433         var innerHTML  = '';
25434         lastnode = '';
25435         while (currentElementChild) {
25436             // Formatting code (indent the tree so it looks nice on the screen)
25437             var nopad = nopadtext;
25438             if (lastnode == 'SPAN') {
25439                 nopad  = true;
25440             }
25441             // text
25442             if  (currentElementChild.nodeName == '#text') {
25443                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25444                 toadd = nopadtext ? toadd : toadd.trim();
25445                 if (!nopad && toadd.length > 80) {
25446                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25447                 }
25448                 innerHTML  += toadd;
25449                 
25450                 i++;
25451                 currentElementChild = currentElement.childNodes.item(i);
25452                 lastNode = '';
25453                 continue;
25454             }
25455             allText = false;
25456             
25457             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25458                 
25459             // Recursively traverse the tree structure of the child node
25460             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25461             lastnode = currentElementChild.nodeName;
25462             i++;
25463             currentElementChild=currentElement.childNodes.item(i);
25464         }
25465         
25466         ret += innerHTML;
25467         
25468         if (!allText) {
25469                 // The remaining code is mostly for formatting the tree
25470             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25471         }
25472         
25473         
25474         if (tagName) {
25475             ret+= "</"+tagName+">";
25476         }
25477         return ret;
25478         
25479     },
25480         
25481     applyBlacklists : function()
25482     {
25483         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25484         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25485         
25486         this.white = [];
25487         this.black = [];
25488         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25489             if (b.indexOf(tag) > -1) {
25490                 return;
25491             }
25492             this.white.push(tag);
25493             
25494         }, this);
25495         
25496         Roo.each(w, function(tag) {
25497             if (b.indexOf(tag) > -1) {
25498                 return;
25499             }
25500             if (this.white.indexOf(tag) > -1) {
25501                 return;
25502             }
25503             this.white.push(tag);
25504             
25505         }, this);
25506         
25507         
25508         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25509             if (w.indexOf(tag) > -1) {
25510                 return;
25511             }
25512             this.black.push(tag);
25513             
25514         }, this);
25515         
25516         Roo.each(b, function(tag) {
25517             if (w.indexOf(tag) > -1) {
25518                 return;
25519             }
25520             if (this.black.indexOf(tag) > -1) {
25521                 return;
25522             }
25523             this.black.push(tag);
25524             
25525         }, this);
25526         
25527         
25528         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25529         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25530         
25531         this.cwhite = [];
25532         this.cblack = [];
25533         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25534             if (b.indexOf(tag) > -1) {
25535                 return;
25536             }
25537             this.cwhite.push(tag);
25538             
25539         }, this);
25540         
25541         Roo.each(w, function(tag) {
25542             if (b.indexOf(tag) > -1) {
25543                 return;
25544             }
25545             if (this.cwhite.indexOf(tag) > -1) {
25546                 return;
25547             }
25548             this.cwhite.push(tag);
25549             
25550         }, this);
25551         
25552         
25553         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25554             if (w.indexOf(tag) > -1) {
25555                 return;
25556             }
25557             this.cblack.push(tag);
25558             
25559         }, this);
25560         
25561         Roo.each(b, function(tag) {
25562             if (w.indexOf(tag) > -1) {
25563                 return;
25564             }
25565             if (this.cblack.indexOf(tag) > -1) {
25566                 return;
25567             }
25568             this.cblack.push(tag);
25569             
25570         }, this);
25571     },
25572     
25573     setStylesheets : function(stylesheets)
25574     {
25575         if(typeof(stylesheets) == 'string'){
25576             Roo.get(this.iframe.contentDocument.head).createChild({
25577                 tag : 'link',
25578                 rel : 'stylesheet',
25579                 type : 'text/css',
25580                 href : stylesheets
25581             });
25582             
25583             return;
25584         }
25585         var _this = this;
25586      
25587         Roo.each(stylesheets, function(s) {
25588             if(!s.length){
25589                 return;
25590             }
25591             
25592             Roo.get(_this.iframe.contentDocument.head).createChild({
25593                 tag : 'link',
25594                 rel : 'stylesheet',
25595                 type : 'text/css',
25596                 href : s
25597             });
25598         });
25599
25600         
25601     },
25602     
25603     removeStylesheets : function()
25604     {
25605         var _this = this;
25606         
25607         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25608             s.remove();
25609         });
25610     },
25611     
25612     setStyle : function(style)
25613     {
25614         Roo.get(this.iframe.contentDocument.head).createChild({
25615             tag : 'style',
25616             type : 'text/css',
25617             html : style
25618         });
25619
25620         return;
25621     }
25622     
25623     // hide stuff that is not compatible
25624     /**
25625      * @event blur
25626      * @hide
25627      */
25628     /**
25629      * @event change
25630      * @hide
25631      */
25632     /**
25633      * @event focus
25634      * @hide
25635      */
25636     /**
25637      * @event specialkey
25638      * @hide
25639      */
25640     /**
25641      * @cfg {String} fieldClass @hide
25642      */
25643     /**
25644      * @cfg {String} focusClass @hide
25645      */
25646     /**
25647      * @cfg {String} autoCreate @hide
25648      */
25649     /**
25650      * @cfg {String} inputType @hide
25651      */
25652     /**
25653      * @cfg {String} invalidClass @hide
25654      */
25655     /**
25656      * @cfg {String} invalidText @hide
25657      */
25658     /**
25659      * @cfg {String} msgFx @hide
25660      */
25661     /**
25662      * @cfg {String} validateOnBlur @hide
25663      */
25664 });
25665
25666 Roo.HtmlEditorCore.white = [
25667         'area', 'br', 'img', 'input', 'hr', 'wbr',
25668         
25669        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25670        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25671        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25672        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25673        'table',   'ul',         'xmp', 
25674        
25675        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25676       'thead',   'tr', 
25677      
25678       'dir', 'menu', 'ol', 'ul', 'dl',
25679        
25680       'embed',  'object'
25681 ];
25682
25683
25684 Roo.HtmlEditorCore.black = [
25685     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25686         'applet', // 
25687         'base',   'basefont', 'bgsound', 'blink',  'body', 
25688         'frame',  'frameset', 'head',    'html',   'ilayer', 
25689         'iframe', 'layer',  'link',     'meta',    'object',   
25690         'script', 'style' ,'title',  'xml' // clean later..
25691 ];
25692 Roo.HtmlEditorCore.clean = [
25693     'script', 'style', 'title', 'xml'
25694 ];
25695 Roo.HtmlEditorCore.remove = [
25696     'font'
25697 ];
25698 // attributes..
25699
25700 Roo.HtmlEditorCore.ablack = [
25701     'on'
25702 ];
25703     
25704 Roo.HtmlEditorCore.aclean = [ 
25705     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25706 ];
25707
25708 // protocols..
25709 Roo.HtmlEditorCore.pwhite= [
25710         'http',  'https',  'mailto'
25711 ];
25712
25713 // white listed style attributes.
25714 Roo.HtmlEditorCore.cwhite= [
25715       //  'text-align', /// default is to allow most things..
25716       
25717          
25718 //        'font-size'//??
25719 ];
25720
25721 // black listed style attributes.
25722 Roo.HtmlEditorCore.cblack= [
25723       //  'font-size' -- this can be set by the project 
25724 ];
25725
25726
25727 Roo.HtmlEditorCore.swapCodes   =[ 
25728     [    8211, "--" ], 
25729     [    8212, "--" ], 
25730     [    8216,  "'" ],  
25731     [    8217, "'" ],  
25732     [    8220, '"' ],  
25733     [    8221, '"' ],  
25734     [    8226, "*" ],  
25735     [    8230, "..." ]
25736 ]; 
25737
25738     /*
25739  * - LGPL
25740  *
25741  * HtmlEditor
25742  * 
25743  */
25744
25745 /**
25746  * @class Roo.bootstrap.HtmlEditor
25747  * @extends Roo.bootstrap.TextArea
25748  * Bootstrap HtmlEditor class
25749
25750  * @constructor
25751  * Create a new HtmlEditor
25752  * @param {Object} config The config object
25753  */
25754
25755 Roo.bootstrap.HtmlEditor = function(config){
25756     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25757     if (!this.toolbars) {
25758         this.toolbars = [];
25759     }
25760     
25761     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25762     this.addEvents({
25763             /**
25764              * @event initialize
25765              * Fires when the editor is fully initialized (including the iframe)
25766              * @param {HtmlEditor} this
25767              */
25768             initialize: true,
25769             /**
25770              * @event activate
25771              * Fires when the editor is first receives the focus. Any insertion must wait
25772              * until after this event.
25773              * @param {HtmlEditor} this
25774              */
25775             activate: true,
25776              /**
25777              * @event beforesync
25778              * Fires before the textarea is updated with content from the editor iframe. Return false
25779              * to cancel the sync.
25780              * @param {HtmlEditor} this
25781              * @param {String} html
25782              */
25783             beforesync: true,
25784              /**
25785              * @event beforepush
25786              * Fires before the iframe editor is updated with content from the textarea. Return false
25787              * to cancel the push.
25788              * @param {HtmlEditor} this
25789              * @param {String} html
25790              */
25791             beforepush: true,
25792              /**
25793              * @event sync
25794              * Fires when the textarea is updated with content from the editor iframe.
25795              * @param {HtmlEditor} this
25796              * @param {String} html
25797              */
25798             sync: true,
25799              /**
25800              * @event push
25801              * Fires when the iframe editor is updated with content from the textarea.
25802              * @param {HtmlEditor} this
25803              * @param {String} html
25804              */
25805             push: true,
25806              /**
25807              * @event editmodechange
25808              * Fires when the editor switches edit modes
25809              * @param {HtmlEditor} this
25810              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25811              */
25812             editmodechange: true,
25813             /**
25814              * @event editorevent
25815              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25816              * @param {HtmlEditor} this
25817              */
25818             editorevent: true,
25819             /**
25820              * @event firstfocus
25821              * Fires when on first focus - needed by toolbars..
25822              * @param {HtmlEditor} this
25823              */
25824             firstfocus: true,
25825             /**
25826              * @event autosave
25827              * Auto save the htmlEditor value as a file into Events
25828              * @param {HtmlEditor} this
25829              */
25830             autosave: true,
25831             /**
25832              * @event savedpreview
25833              * preview the saved version of htmlEditor
25834              * @param {HtmlEditor} this
25835              */
25836             savedpreview: true
25837         });
25838 };
25839
25840
25841 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25842     
25843     
25844       /**
25845      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25846      */
25847     toolbars : false,
25848     
25849      /**
25850     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25851     */
25852     btns : [],
25853    
25854      /**
25855      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25856      *                        Roo.resizable.
25857      */
25858     resizable : false,
25859      /**
25860      * @cfg {Number} height (in pixels)
25861      */   
25862     height: 300,
25863    /**
25864      * @cfg {Number} width (in pixels)
25865      */   
25866     width: false,
25867     
25868     /**
25869      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25870      * 
25871      */
25872     stylesheets: false,
25873     
25874     // id of frame..
25875     frameId: false,
25876     
25877     // private properties
25878     validationEvent : false,
25879     deferHeight: true,
25880     initialized : false,
25881     activated : false,
25882     
25883     onFocus : Roo.emptyFn,
25884     iframePad:3,
25885     hideMode:'offsets',
25886     
25887     tbContainer : false,
25888     
25889     bodyCls : '',
25890     
25891     toolbarContainer :function() {
25892         return this.wrap.select('.x-html-editor-tb',true).first();
25893     },
25894
25895     /**
25896      * Protected method that will not generally be called directly. It
25897      * is called when the editor creates its toolbar. Override this method if you need to
25898      * add custom toolbar buttons.
25899      * @param {HtmlEditor} editor
25900      */
25901     createToolbar : function(){
25902         Roo.log('renewing');
25903         Roo.log("create toolbars");
25904         
25905         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25906         this.toolbars[0].render(this.toolbarContainer());
25907         
25908         return;
25909         
25910 //        if (!editor.toolbars || !editor.toolbars.length) {
25911 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25912 //        }
25913 //        
25914 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25915 //            editor.toolbars[i] = Roo.factory(
25916 //                    typeof(editor.toolbars[i]) == 'string' ?
25917 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25918 //                Roo.bootstrap.HtmlEditor);
25919 //            editor.toolbars[i].init(editor);
25920 //        }
25921     },
25922
25923      
25924     // private
25925     onRender : function(ct, position)
25926     {
25927        // Roo.log("Call onRender: " + this.xtype);
25928         var _t = this;
25929         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25930       
25931         this.wrap = this.inputEl().wrap({
25932             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25933         });
25934         
25935         this.editorcore.onRender(ct, position);
25936          
25937         if (this.resizable) {
25938             this.resizeEl = new Roo.Resizable(this.wrap, {
25939                 pinned : true,
25940                 wrap: true,
25941                 dynamic : true,
25942                 minHeight : this.height,
25943                 height: this.height,
25944                 handles : this.resizable,
25945                 width: this.width,
25946                 listeners : {
25947                     resize : function(r, w, h) {
25948                         _t.onResize(w,h); // -something
25949                     }
25950                 }
25951             });
25952             
25953         }
25954         this.createToolbar(this);
25955        
25956         
25957         if(!this.width && this.resizable){
25958             this.setSize(this.wrap.getSize());
25959         }
25960         if (this.resizeEl) {
25961             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25962             // should trigger onReize..
25963         }
25964         
25965     },
25966
25967     // private
25968     onResize : function(w, h)
25969     {
25970         Roo.log('resize: ' +w + ',' + h );
25971         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25972         var ew = false;
25973         var eh = false;
25974         
25975         if(this.inputEl() ){
25976             if(typeof w == 'number'){
25977                 var aw = w - this.wrap.getFrameWidth('lr');
25978                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25979                 ew = aw;
25980             }
25981             if(typeof h == 'number'){
25982                  var tbh = -11;  // fixme it needs to tool bar size!
25983                 for (var i =0; i < this.toolbars.length;i++) {
25984                     // fixme - ask toolbars for heights?
25985                     tbh += this.toolbars[i].el.getHeight();
25986                     //if (this.toolbars[i].footer) {
25987                     //    tbh += this.toolbars[i].footer.el.getHeight();
25988                     //}
25989                 }
25990               
25991                 
25992                 
25993                 
25994                 
25995                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25996                 ah -= 5; // knock a few pixes off for look..
25997                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25998                 var eh = ah;
25999             }
26000         }
26001         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26002         this.editorcore.onResize(ew,eh);
26003         
26004     },
26005
26006     /**
26007      * Toggles the editor between standard and source edit mode.
26008      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26009      */
26010     toggleSourceEdit : function(sourceEditMode)
26011     {
26012         this.editorcore.toggleSourceEdit(sourceEditMode);
26013         
26014         if(this.editorcore.sourceEditMode){
26015             Roo.log('editor - showing textarea');
26016             
26017 //            Roo.log('in');
26018 //            Roo.log(this.syncValue());
26019             this.syncValue();
26020             this.inputEl().removeClass(['hide', 'x-hidden']);
26021             this.inputEl().dom.removeAttribute('tabIndex');
26022             this.inputEl().focus();
26023         }else{
26024             Roo.log('editor - hiding textarea');
26025 //            Roo.log('out')
26026 //            Roo.log(this.pushValue()); 
26027             this.pushValue();
26028             
26029             this.inputEl().addClass(['hide', 'x-hidden']);
26030             this.inputEl().dom.setAttribute('tabIndex', -1);
26031             //this.deferFocus();
26032         }
26033          
26034         if(this.resizable){
26035             this.setSize(this.wrap.getSize());
26036         }
26037         
26038         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26039     },
26040  
26041     // private (for BoxComponent)
26042     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26043
26044     // private (for BoxComponent)
26045     getResizeEl : function(){
26046         return this.wrap;
26047     },
26048
26049     // private (for BoxComponent)
26050     getPositionEl : function(){
26051         return this.wrap;
26052     },
26053
26054     // private
26055     initEvents : function(){
26056         this.originalValue = this.getValue();
26057     },
26058
26059 //    /**
26060 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26061 //     * @method
26062 //     */
26063 //    markInvalid : Roo.emptyFn,
26064 //    /**
26065 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26066 //     * @method
26067 //     */
26068 //    clearInvalid : Roo.emptyFn,
26069
26070     setValue : function(v){
26071         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26072         this.editorcore.pushValue();
26073     },
26074
26075      
26076     // private
26077     deferFocus : function(){
26078         this.focus.defer(10, this);
26079     },
26080
26081     // doc'ed in Field
26082     focus : function(){
26083         this.editorcore.focus();
26084         
26085     },
26086       
26087
26088     // private
26089     onDestroy : function(){
26090         
26091         
26092         
26093         if(this.rendered){
26094             
26095             for (var i =0; i < this.toolbars.length;i++) {
26096                 // fixme - ask toolbars for heights?
26097                 this.toolbars[i].onDestroy();
26098             }
26099             
26100             this.wrap.dom.innerHTML = '';
26101             this.wrap.remove();
26102         }
26103     },
26104
26105     // private
26106     onFirstFocus : function(){
26107         //Roo.log("onFirstFocus");
26108         this.editorcore.onFirstFocus();
26109          for (var i =0; i < this.toolbars.length;i++) {
26110             this.toolbars[i].onFirstFocus();
26111         }
26112         
26113     },
26114     
26115     // private
26116     syncValue : function()
26117     {   
26118         this.editorcore.syncValue();
26119     },
26120     
26121     pushValue : function()
26122     {   
26123         this.editorcore.pushValue();
26124     }
26125      
26126     
26127     // hide stuff that is not compatible
26128     /**
26129      * @event blur
26130      * @hide
26131      */
26132     /**
26133      * @event change
26134      * @hide
26135      */
26136     /**
26137      * @event focus
26138      * @hide
26139      */
26140     /**
26141      * @event specialkey
26142      * @hide
26143      */
26144     /**
26145      * @cfg {String} fieldClass @hide
26146      */
26147     /**
26148      * @cfg {String} focusClass @hide
26149      */
26150     /**
26151      * @cfg {String} autoCreate @hide
26152      */
26153     /**
26154      * @cfg {String} inputType @hide
26155      */
26156      
26157     /**
26158      * @cfg {String} invalidText @hide
26159      */
26160     /**
26161      * @cfg {String} msgFx @hide
26162      */
26163     /**
26164      * @cfg {String} validateOnBlur @hide
26165      */
26166 });
26167  
26168     
26169    
26170    
26171    
26172       
26173 Roo.namespace('Roo.bootstrap.htmleditor');
26174 /**
26175  * @class Roo.bootstrap.HtmlEditorToolbar1
26176  * Basic Toolbar
26177  * 
26178  * @example
26179  * Usage:
26180  *
26181  new Roo.bootstrap.HtmlEditor({
26182     ....
26183     toolbars : [
26184         new Roo.bootstrap.HtmlEditorToolbar1({
26185             disable : { fonts: 1 , format: 1, ..., ... , ...],
26186             btns : [ .... ]
26187         })
26188     }
26189      
26190  * 
26191  * @cfg {Object} disable List of elements to disable..
26192  * @cfg {Array} btns List of additional buttons.
26193  * 
26194  * 
26195  * NEEDS Extra CSS? 
26196  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26197  */
26198  
26199 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26200 {
26201     
26202     Roo.apply(this, config);
26203     
26204     // default disabled, based on 'good practice'..
26205     this.disable = this.disable || {};
26206     Roo.applyIf(this.disable, {
26207         fontSize : true,
26208         colors : true,
26209         specialElements : true
26210     });
26211     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26212     
26213     this.editor = config.editor;
26214     this.editorcore = config.editor.editorcore;
26215     
26216     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26217     
26218     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26219     // dont call parent... till later.
26220 }
26221 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26222      
26223     bar : true,
26224     
26225     editor : false,
26226     editorcore : false,
26227     
26228     
26229     formats : [
26230         "p" ,  
26231         "h1","h2","h3","h4","h5","h6", 
26232         "pre", "code", 
26233         "abbr", "acronym", "address", "cite", "samp", "var",
26234         'div','span'
26235     ],
26236     
26237     onRender : function(ct, position)
26238     {
26239        // Roo.log("Call onRender: " + this.xtype);
26240         
26241        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26242        Roo.log(this.el);
26243        this.el.dom.style.marginBottom = '0';
26244        var _this = this;
26245        var editorcore = this.editorcore;
26246        var editor= this.editor;
26247        
26248        var children = [];
26249        var btn = function(id,cmd , toggle, handler, html){
26250        
26251             var  event = toggle ? 'toggle' : 'click';
26252        
26253             var a = {
26254                 size : 'sm',
26255                 xtype: 'Button',
26256                 xns: Roo.bootstrap,
26257                 //glyphicon : id,
26258                 fa: id,
26259                 cmd : id || cmd,
26260                 enableToggle:toggle !== false,
26261                 html : html || '',
26262                 pressed : toggle ? false : null,
26263                 listeners : {}
26264             };
26265             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26266                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26267             };
26268             children.push(a);
26269             return a;
26270        }
26271        
26272     //    var cb_box = function...
26273         
26274         var style = {
26275                 xtype: 'Button',
26276                 size : 'sm',
26277                 xns: Roo.bootstrap,
26278                 fa : 'font',
26279                 //html : 'submit'
26280                 menu : {
26281                     xtype: 'Menu',
26282                     xns: Roo.bootstrap,
26283                     items:  []
26284                 }
26285         };
26286         Roo.each(this.formats, function(f) {
26287             style.menu.items.push({
26288                 xtype :'MenuItem',
26289                 xns: Roo.bootstrap,
26290                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26291                 tagname : f,
26292                 listeners : {
26293                     click : function()
26294                     {
26295                         editorcore.insertTag(this.tagname);
26296                         editor.focus();
26297                     }
26298                 }
26299                 
26300             });
26301         });
26302         children.push(style);   
26303         
26304         btn('bold',false,true);
26305         btn('italic',false,true);
26306         btn('align-left', 'justifyleft',true);
26307         btn('align-center', 'justifycenter',true);
26308         btn('align-right' , 'justifyright',true);
26309         btn('link', false, false, function(btn) {
26310             //Roo.log("create link?");
26311             var url = prompt(this.createLinkText, this.defaultLinkValue);
26312             if(url && url != 'http:/'+'/'){
26313                 this.editorcore.relayCmd('createlink', url);
26314             }
26315         }),
26316         btn('list','insertunorderedlist',true);
26317         btn('pencil', false,true, function(btn){
26318                 Roo.log(this);
26319                 this.toggleSourceEdit(btn.pressed);
26320         });
26321         
26322         if (this.editor.btns.length > 0) {
26323             for (var i = 0; i<this.editor.btns.length; i++) {
26324                 children.push(this.editor.btns[i]);
26325             }
26326         }
26327         
26328         /*
26329         var cog = {
26330                 xtype: 'Button',
26331                 size : 'sm',
26332                 xns: Roo.bootstrap,
26333                 glyphicon : 'cog',
26334                 //html : 'submit'
26335                 menu : {
26336                     xtype: 'Menu',
26337                     xns: Roo.bootstrap,
26338                     items:  []
26339                 }
26340         };
26341         
26342         cog.menu.items.push({
26343             xtype :'MenuItem',
26344             xns: Roo.bootstrap,
26345             html : Clean styles,
26346             tagname : f,
26347             listeners : {
26348                 click : function()
26349                 {
26350                     editorcore.insertTag(this.tagname);
26351                     editor.focus();
26352                 }
26353             }
26354             
26355         });
26356        */
26357         
26358          
26359        this.xtype = 'NavSimplebar';
26360         
26361         for(var i=0;i< children.length;i++) {
26362             
26363             this.buttons.add(this.addxtypeChild(children[i]));
26364             
26365         }
26366         
26367         editor.on('editorevent', this.updateToolbar, this);
26368     },
26369     onBtnClick : function(id)
26370     {
26371        this.editorcore.relayCmd(id);
26372        this.editorcore.focus();
26373     },
26374     
26375     /**
26376      * Protected method that will not generally be called directly. It triggers
26377      * a toolbar update by reading the markup state of the current selection in the editor.
26378      */
26379     updateToolbar: function(){
26380
26381         if(!this.editorcore.activated){
26382             this.editor.onFirstFocus(); // is this neeed?
26383             return;
26384         }
26385
26386         var btns = this.buttons; 
26387         var doc = this.editorcore.doc;
26388         btns.get('bold').setActive(doc.queryCommandState('bold'));
26389         btns.get('italic').setActive(doc.queryCommandState('italic'));
26390         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26391         
26392         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26393         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26394         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26395         
26396         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26397         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26398          /*
26399         
26400         var ans = this.editorcore.getAllAncestors();
26401         if (this.formatCombo) {
26402             
26403             
26404             var store = this.formatCombo.store;
26405             this.formatCombo.setValue("");
26406             for (var i =0; i < ans.length;i++) {
26407                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26408                     // select it..
26409                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26410                     break;
26411                 }
26412             }
26413         }
26414         
26415         
26416         
26417         // hides menus... - so this cant be on a menu...
26418         Roo.bootstrap.MenuMgr.hideAll();
26419         */
26420         Roo.bootstrap.MenuMgr.hideAll();
26421         //this.editorsyncValue();
26422     },
26423     onFirstFocus: function() {
26424         this.buttons.each(function(item){
26425            item.enable();
26426         });
26427     },
26428     toggleSourceEdit : function(sourceEditMode){
26429         
26430           
26431         if(sourceEditMode){
26432             Roo.log("disabling buttons");
26433            this.buttons.each( function(item){
26434                 if(item.cmd != 'pencil'){
26435                     item.disable();
26436                 }
26437             });
26438           
26439         }else{
26440             Roo.log("enabling buttons");
26441             if(this.editorcore.initialized){
26442                 this.buttons.each( function(item){
26443                     item.enable();
26444                 });
26445             }
26446             
26447         }
26448         Roo.log("calling toggole on editor");
26449         // tell the editor that it's been pressed..
26450         this.editor.toggleSourceEdit(sourceEditMode);
26451        
26452     }
26453 });
26454
26455
26456
26457
26458  
26459 /*
26460  * - LGPL
26461  */
26462
26463 /**
26464  * @class Roo.bootstrap.Markdown
26465  * @extends Roo.bootstrap.TextArea
26466  * Bootstrap Showdown editable area
26467  * @cfg {string} content
26468  * 
26469  * @constructor
26470  * Create a new Showdown
26471  */
26472
26473 Roo.bootstrap.Markdown = function(config){
26474     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26475    
26476 };
26477
26478 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26479     
26480     editing :false,
26481     
26482     initEvents : function()
26483     {
26484         
26485         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26486         this.markdownEl = this.el.createChild({
26487             cls : 'roo-markdown-area'
26488         });
26489         this.inputEl().addClass('d-none');
26490         if (this.getValue() == '') {
26491             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26492             
26493         } else {
26494             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26495         }
26496         this.markdownEl.on('click', this.toggleTextEdit, this);
26497         this.on('blur', this.toggleTextEdit, this);
26498         this.on('specialkey', this.resizeTextArea, this);
26499     },
26500     
26501     toggleTextEdit : function()
26502     {
26503         var sh = this.markdownEl.getHeight();
26504         this.inputEl().addClass('d-none');
26505         this.markdownEl.addClass('d-none');
26506         if (!this.editing) {
26507             // show editor?
26508             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26509             this.inputEl().removeClass('d-none');
26510             this.inputEl().focus();
26511             this.editing = true;
26512             return;
26513         }
26514         // show showdown...
26515         this.updateMarkdown();
26516         this.markdownEl.removeClass('d-none');
26517         this.editing = false;
26518         return;
26519     },
26520     updateMarkdown : function()
26521     {
26522         if (this.getValue() == '') {
26523             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26524             return;
26525         }
26526  
26527         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26528     },
26529     
26530     resizeTextArea: function () {
26531         
26532         var sh = 100;
26533         Roo.log([sh, this.getValue().split("\n").length * 30]);
26534         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26535     },
26536     setValue : function(val)
26537     {
26538         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26539         if (!this.editing) {
26540             this.updateMarkdown();
26541         }
26542         
26543     },
26544     focus : function()
26545     {
26546         if (!this.editing) {
26547             this.toggleTextEdit();
26548         }
26549         
26550     }
26551
26552
26553 });
26554 /**
26555  * @class Roo.bootstrap.Table.AbstractSelectionModel
26556  * @extends Roo.util.Observable
26557  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26558  * implemented by descendant classes.  This class should not be directly instantiated.
26559  * @constructor
26560  */
26561 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26562     this.locked = false;
26563     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26564 };
26565
26566
26567 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26568     /** @ignore Called by the grid automatically. Do not call directly. */
26569     init : function(grid){
26570         this.grid = grid;
26571         this.initEvents();
26572     },
26573
26574     /**
26575      * Locks the selections.
26576      */
26577     lock : function(){
26578         this.locked = true;
26579     },
26580
26581     /**
26582      * Unlocks the selections.
26583      */
26584     unlock : function(){
26585         this.locked = false;
26586     },
26587
26588     /**
26589      * Returns true if the selections are locked.
26590      * @return {Boolean}
26591      */
26592     isLocked : function(){
26593         return this.locked;
26594     },
26595     
26596     
26597     initEvents : function ()
26598     {
26599         
26600     }
26601 });
26602 /**
26603  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26604  * @class Roo.bootstrap.Table.RowSelectionModel
26605  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26606  * It supports multiple selections and keyboard selection/navigation. 
26607  * @constructor
26608  * @param {Object} config
26609  */
26610
26611 Roo.bootstrap.Table.RowSelectionModel = function(config){
26612     Roo.apply(this, config);
26613     this.selections = new Roo.util.MixedCollection(false, function(o){
26614         return o.id;
26615     });
26616
26617     this.last = false;
26618     this.lastActive = false;
26619
26620     this.addEvents({
26621         /**
26622              * @event selectionchange
26623              * Fires when the selection changes
26624              * @param {SelectionModel} this
26625              */
26626             "selectionchange" : true,
26627         /**
26628              * @event afterselectionchange
26629              * Fires after the selection changes (eg. by key press or clicking)
26630              * @param {SelectionModel} this
26631              */
26632             "afterselectionchange" : true,
26633         /**
26634              * @event beforerowselect
26635              * Fires when a row is selected being selected, return false to cancel.
26636              * @param {SelectionModel} this
26637              * @param {Number} rowIndex The selected index
26638              * @param {Boolean} keepExisting False if other selections will be cleared
26639              */
26640             "beforerowselect" : true,
26641         /**
26642              * @event rowselect
26643              * Fires when a row is selected.
26644              * @param {SelectionModel} this
26645              * @param {Number} rowIndex The selected index
26646              * @param {Roo.data.Record} r The record
26647              */
26648             "rowselect" : true,
26649         /**
26650              * @event rowdeselect
26651              * Fires when a row is deselected.
26652              * @param {SelectionModel} this
26653              * @param {Number} rowIndex The selected index
26654              */
26655         "rowdeselect" : true
26656     });
26657     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26658     this.locked = false;
26659  };
26660
26661 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26662     /**
26663      * @cfg {Boolean} singleSelect
26664      * True to allow selection of only one row at a time (defaults to false)
26665      */
26666     singleSelect : false,
26667
26668     // private
26669     initEvents : function()
26670     {
26671
26672         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26673         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26674         //}else{ // allow click to work like normal
26675          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26676         //}
26677         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26678         this.grid.on("rowclick", this.handleMouseDown, this);
26679         
26680         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26681             "up" : function(e){
26682                 if(!e.shiftKey){
26683                     this.selectPrevious(e.shiftKey);
26684                 }else if(this.last !== false && this.lastActive !== false){
26685                     var last = this.last;
26686                     this.selectRange(this.last,  this.lastActive-1);
26687                     this.grid.getView().focusRow(this.lastActive);
26688                     if(last !== false){
26689                         this.last = last;
26690                     }
26691                 }else{
26692                     this.selectFirstRow();
26693                 }
26694                 this.fireEvent("afterselectionchange", this);
26695             },
26696             "down" : function(e){
26697                 if(!e.shiftKey){
26698                     this.selectNext(e.shiftKey);
26699                 }else if(this.last !== false && this.lastActive !== false){
26700                     var last = this.last;
26701                     this.selectRange(this.last,  this.lastActive+1);
26702                     this.grid.getView().focusRow(this.lastActive);
26703                     if(last !== false){
26704                         this.last = last;
26705                     }
26706                 }else{
26707                     this.selectFirstRow();
26708                 }
26709                 this.fireEvent("afterselectionchange", this);
26710             },
26711             scope: this
26712         });
26713         this.grid.store.on('load', function(){
26714             this.selections.clear();
26715         },this);
26716         /*
26717         var view = this.grid.view;
26718         view.on("refresh", this.onRefresh, this);
26719         view.on("rowupdated", this.onRowUpdated, this);
26720         view.on("rowremoved", this.onRemove, this);
26721         */
26722     },
26723
26724     // private
26725     onRefresh : function()
26726     {
26727         var ds = this.grid.store, i, v = this.grid.view;
26728         var s = this.selections;
26729         s.each(function(r){
26730             if((i = ds.indexOfId(r.id)) != -1){
26731                 v.onRowSelect(i);
26732             }else{
26733                 s.remove(r);
26734             }
26735         });
26736     },
26737
26738     // private
26739     onRemove : function(v, index, r){
26740         this.selections.remove(r);
26741     },
26742
26743     // private
26744     onRowUpdated : function(v, index, r){
26745         if(this.isSelected(r)){
26746             v.onRowSelect(index);
26747         }
26748     },
26749
26750     /**
26751      * Select records.
26752      * @param {Array} records The records to select
26753      * @param {Boolean} keepExisting (optional) True to keep existing selections
26754      */
26755     selectRecords : function(records, keepExisting)
26756     {
26757         if(!keepExisting){
26758             this.clearSelections();
26759         }
26760             var ds = this.grid.store;
26761         for(var i = 0, len = records.length; i < len; i++){
26762             this.selectRow(ds.indexOf(records[i]), true);
26763         }
26764     },
26765
26766     /**
26767      * Gets the number of selected rows.
26768      * @return {Number}
26769      */
26770     getCount : function(){
26771         return this.selections.length;
26772     },
26773
26774     /**
26775      * Selects the first row in the grid.
26776      */
26777     selectFirstRow : function(){
26778         this.selectRow(0);
26779     },
26780
26781     /**
26782      * Select the last row.
26783      * @param {Boolean} keepExisting (optional) True to keep existing selections
26784      */
26785     selectLastRow : function(keepExisting){
26786         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26787         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26788     },
26789
26790     /**
26791      * Selects the row immediately following the last selected row.
26792      * @param {Boolean} keepExisting (optional) True to keep existing selections
26793      */
26794     selectNext : function(keepExisting)
26795     {
26796             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26797             this.selectRow(this.last+1, keepExisting);
26798             this.grid.getView().focusRow(this.last);
26799         }
26800     },
26801
26802     /**
26803      * Selects the row that precedes the last selected row.
26804      * @param {Boolean} keepExisting (optional) True to keep existing selections
26805      */
26806     selectPrevious : function(keepExisting){
26807         if(this.last){
26808             this.selectRow(this.last-1, keepExisting);
26809             this.grid.getView().focusRow(this.last);
26810         }
26811     },
26812
26813     /**
26814      * Returns the selected records
26815      * @return {Array} Array of selected records
26816      */
26817     getSelections : function(){
26818         return [].concat(this.selections.items);
26819     },
26820
26821     /**
26822      * Returns the first selected record.
26823      * @return {Record}
26824      */
26825     getSelected : function(){
26826         return this.selections.itemAt(0);
26827     },
26828
26829
26830     /**
26831      * Clears all selections.
26832      */
26833     clearSelections : function(fast)
26834     {
26835         if(this.locked) {
26836             return;
26837         }
26838         if(fast !== true){
26839                 var ds = this.grid.store;
26840             var s = this.selections;
26841             s.each(function(r){
26842                 this.deselectRow(ds.indexOfId(r.id));
26843             }, this);
26844             s.clear();
26845         }else{
26846             this.selections.clear();
26847         }
26848         this.last = false;
26849     },
26850
26851
26852     /**
26853      * Selects all rows.
26854      */
26855     selectAll : function(){
26856         if(this.locked) {
26857             return;
26858         }
26859         this.selections.clear();
26860         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26861             this.selectRow(i, true);
26862         }
26863     },
26864
26865     /**
26866      * Returns True if there is a selection.
26867      * @return {Boolean}
26868      */
26869     hasSelection : function(){
26870         return this.selections.length > 0;
26871     },
26872
26873     /**
26874      * Returns True if the specified row is selected.
26875      * @param {Number/Record} record The record or index of the record to check
26876      * @return {Boolean}
26877      */
26878     isSelected : function(index){
26879             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26880         return (r && this.selections.key(r.id) ? true : false);
26881     },
26882
26883     /**
26884      * Returns True if the specified record id is selected.
26885      * @param {String} id The id of record to check
26886      * @return {Boolean}
26887      */
26888     isIdSelected : function(id){
26889         return (this.selections.key(id) ? true : false);
26890     },
26891
26892
26893     // private
26894     handleMouseDBClick : function(e, t){
26895         
26896     },
26897     // private
26898     handleMouseDown : function(e, t)
26899     {
26900             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26901         if(this.isLocked() || rowIndex < 0 ){
26902             return;
26903         };
26904         if(e.shiftKey && this.last !== false){
26905             var last = this.last;
26906             this.selectRange(last, rowIndex, e.ctrlKey);
26907             this.last = last; // reset the last
26908             t.focus();
26909     
26910         }else{
26911             var isSelected = this.isSelected(rowIndex);
26912             //Roo.log("select row:" + rowIndex);
26913             if(isSelected){
26914                 this.deselectRow(rowIndex);
26915             } else {
26916                         this.selectRow(rowIndex, true);
26917             }
26918     
26919             /*
26920                 if(e.button !== 0 && isSelected){
26921                 alert('rowIndex 2: ' + rowIndex);
26922                     view.focusRow(rowIndex);
26923                 }else if(e.ctrlKey && isSelected){
26924                     this.deselectRow(rowIndex);
26925                 }else if(!isSelected){
26926                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26927                     view.focusRow(rowIndex);
26928                 }
26929             */
26930         }
26931         this.fireEvent("afterselectionchange", this);
26932     },
26933     // private
26934     handleDragableRowClick :  function(grid, rowIndex, e) 
26935     {
26936         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26937             this.selectRow(rowIndex, false);
26938             grid.view.focusRow(rowIndex);
26939              this.fireEvent("afterselectionchange", this);
26940         }
26941     },
26942     
26943     /**
26944      * Selects multiple rows.
26945      * @param {Array} rows Array of the indexes of the row to select
26946      * @param {Boolean} keepExisting (optional) True to keep existing selections
26947      */
26948     selectRows : function(rows, keepExisting){
26949         if(!keepExisting){
26950             this.clearSelections();
26951         }
26952         for(var i = 0, len = rows.length; i < len; i++){
26953             this.selectRow(rows[i], true);
26954         }
26955     },
26956
26957     /**
26958      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26959      * @param {Number} startRow The index of the first row in the range
26960      * @param {Number} endRow The index of the last row in the range
26961      * @param {Boolean} keepExisting (optional) True to retain existing selections
26962      */
26963     selectRange : function(startRow, endRow, keepExisting){
26964         if(this.locked) {
26965             return;
26966         }
26967         if(!keepExisting){
26968             this.clearSelections();
26969         }
26970         if(startRow <= endRow){
26971             for(var i = startRow; i <= endRow; i++){
26972                 this.selectRow(i, true);
26973             }
26974         }else{
26975             for(var i = startRow; i >= endRow; i--){
26976                 this.selectRow(i, true);
26977             }
26978         }
26979     },
26980
26981     /**
26982      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26983      * @param {Number} startRow The index of the first row in the range
26984      * @param {Number} endRow The index of the last row in the range
26985      */
26986     deselectRange : function(startRow, endRow, preventViewNotify){
26987         if(this.locked) {
26988             return;
26989         }
26990         for(var i = startRow; i <= endRow; i++){
26991             this.deselectRow(i, preventViewNotify);
26992         }
26993     },
26994
26995     /**
26996      * Selects a row.
26997      * @param {Number} row The index of the row to select
26998      * @param {Boolean} keepExisting (optional) True to keep existing selections
26999      */
27000     selectRow : function(index, keepExisting, preventViewNotify)
27001     {
27002             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27003             return;
27004         }
27005         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27006             if(!keepExisting || this.singleSelect){
27007                 this.clearSelections();
27008             }
27009             
27010             var r = this.grid.store.getAt(index);
27011             //console.log('selectRow - record id :' + r.id);
27012             
27013             this.selections.add(r);
27014             this.last = this.lastActive = index;
27015             if(!preventViewNotify){
27016                 var proxy = new Roo.Element(
27017                                 this.grid.getRowDom(index)
27018                 );
27019                 proxy.addClass('bg-info info');
27020             }
27021             this.fireEvent("rowselect", this, index, r);
27022             this.fireEvent("selectionchange", this);
27023         }
27024     },
27025
27026     /**
27027      * Deselects a row.
27028      * @param {Number} row The index of the row to deselect
27029      */
27030     deselectRow : function(index, preventViewNotify)
27031     {
27032         if(this.locked) {
27033             return;
27034         }
27035         if(this.last == index){
27036             this.last = false;
27037         }
27038         if(this.lastActive == index){
27039             this.lastActive = false;
27040         }
27041         
27042         var r = this.grid.store.getAt(index);
27043         if (!r) {
27044             return;
27045         }
27046         
27047         this.selections.remove(r);
27048         //.console.log('deselectRow - record id :' + r.id);
27049         if(!preventViewNotify){
27050         
27051             var proxy = new Roo.Element(
27052                 this.grid.getRowDom(index)
27053             );
27054             proxy.removeClass('bg-info info');
27055         }
27056         this.fireEvent("rowdeselect", this, index);
27057         this.fireEvent("selectionchange", this);
27058     },
27059
27060     // private
27061     restoreLast : function(){
27062         if(this._last){
27063             this.last = this._last;
27064         }
27065     },
27066
27067     // private
27068     acceptsNav : function(row, col, cm){
27069         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27070     },
27071
27072     // private
27073     onEditorKey : function(field, e){
27074         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27075         if(k == e.TAB){
27076             e.stopEvent();
27077             ed.completeEdit();
27078             if(e.shiftKey){
27079                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27080             }else{
27081                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27082             }
27083         }else if(k == e.ENTER && !e.ctrlKey){
27084             e.stopEvent();
27085             ed.completeEdit();
27086             if(e.shiftKey){
27087                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27088             }else{
27089                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27090             }
27091         }else if(k == e.ESC){
27092             ed.cancelEdit();
27093         }
27094         if(newCell){
27095             g.startEditing(newCell[0], newCell[1]);
27096         }
27097     }
27098 });
27099 /*
27100  * Based on:
27101  * Ext JS Library 1.1.1
27102  * Copyright(c) 2006-2007, Ext JS, LLC.
27103  *
27104  * Originally Released Under LGPL - original licence link has changed is not relivant.
27105  *
27106  * Fork - LGPL
27107  * <script type="text/javascript">
27108  */
27109  
27110 /**
27111  * @class Roo.bootstrap.PagingToolbar
27112  * @extends Roo.bootstrap.NavSimplebar
27113  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27114  * @constructor
27115  * Create a new PagingToolbar
27116  * @param {Object} config The config object
27117  * @param {Roo.data.Store} store
27118  */
27119 Roo.bootstrap.PagingToolbar = function(config)
27120 {
27121     // old args format still supported... - xtype is prefered..
27122         // created from xtype...
27123     
27124     this.ds = config.dataSource;
27125     
27126     if (config.store && !this.ds) {
27127         this.store= Roo.factory(config.store, Roo.data);
27128         this.ds = this.store;
27129         this.ds.xmodule = this.xmodule || false;
27130     }
27131     
27132     this.toolbarItems = [];
27133     if (config.items) {
27134         this.toolbarItems = config.items;
27135     }
27136     
27137     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27138     
27139     this.cursor = 0;
27140     
27141     if (this.ds) { 
27142         this.bind(this.ds);
27143     }
27144     
27145     if (Roo.bootstrap.version == 4) {
27146         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27147     } else {
27148         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27149     }
27150     
27151 };
27152
27153 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27154     /**
27155      * @cfg {Roo.data.Store} dataSource
27156      * The underlying data store providing the paged data
27157      */
27158     /**
27159      * @cfg {String/HTMLElement/Element} container
27160      * container The id or element that will contain the toolbar
27161      */
27162     /**
27163      * @cfg {Boolean} displayInfo
27164      * True to display the displayMsg (defaults to false)
27165      */
27166     /**
27167      * @cfg {Number} pageSize
27168      * The number of records to display per page (defaults to 20)
27169      */
27170     pageSize: 20,
27171     /**
27172      * @cfg {String} displayMsg
27173      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27174      */
27175     displayMsg : 'Displaying {0} - {1} of {2}',
27176     /**
27177      * @cfg {String} emptyMsg
27178      * The message to display when no records are found (defaults to "No data to display")
27179      */
27180     emptyMsg : 'No data to display',
27181     /**
27182      * Customizable piece of the default paging text (defaults to "Page")
27183      * @type String
27184      */
27185     beforePageText : "Page",
27186     /**
27187      * Customizable piece of the default paging text (defaults to "of %0")
27188      * @type String
27189      */
27190     afterPageText : "of {0}",
27191     /**
27192      * Customizable piece of the default paging text (defaults to "First Page")
27193      * @type String
27194      */
27195     firstText : "First Page",
27196     /**
27197      * Customizable piece of the default paging text (defaults to "Previous Page")
27198      * @type String
27199      */
27200     prevText : "Previous Page",
27201     /**
27202      * Customizable piece of the default paging text (defaults to "Next Page")
27203      * @type String
27204      */
27205     nextText : "Next Page",
27206     /**
27207      * Customizable piece of the default paging text (defaults to "Last Page")
27208      * @type String
27209      */
27210     lastText : "Last Page",
27211     /**
27212      * Customizable piece of the default paging text (defaults to "Refresh")
27213      * @type String
27214      */
27215     refreshText : "Refresh",
27216
27217     buttons : false,
27218     // private
27219     onRender : function(ct, position) 
27220     {
27221         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27222         this.navgroup.parentId = this.id;
27223         this.navgroup.onRender(this.el, null);
27224         // add the buttons to the navgroup
27225         
27226         if(this.displayInfo){
27227             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27228             this.displayEl = this.el.select('.x-paging-info', true).first();
27229 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27230 //            this.displayEl = navel.el.select('span',true).first();
27231         }
27232         
27233         var _this = this;
27234         
27235         if(this.buttons){
27236             Roo.each(_this.buttons, function(e){ // this might need to use render????
27237                Roo.factory(e).render(_this.el);
27238             });
27239         }
27240             
27241         Roo.each(_this.toolbarItems, function(e) {
27242             _this.navgroup.addItem(e);
27243         });
27244         
27245         
27246         this.first = this.navgroup.addItem({
27247             tooltip: this.firstText,
27248             cls: "prev btn-outline-secondary",
27249             html : ' <i class="fa fa-step-backward"></i>',
27250             disabled: true,
27251             preventDefault: true,
27252             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27253         });
27254         
27255         this.prev =  this.navgroup.addItem({
27256             tooltip: this.prevText,
27257             cls: "prev btn-outline-secondary",
27258             html : ' <i class="fa fa-backward"></i>',
27259             disabled: true,
27260             preventDefault: true,
27261             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27262         });
27263     //this.addSeparator();
27264         
27265         
27266         var field = this.navgroup.addItem( {
27267             tagtype : 'span',
27268             cls : 'x-paging-position  btn-outline-secondary',
27269              disabled: true,
27270             html : this.beforePageText  +
27271                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27272                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27273          } ); //?? escaped?
27274         
27275         this.field = field.el.select('input', true).first();
27276         this.field.on("keydown", this.onPagingKeydown, this);
27277         this.field.on("focus", function(){this.dom.select();});
27278     
27279     
27280         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27281         //this.field.setHeight(18);
27282         //this.addSeparator();
27283         this.next = this.navgroup.addItem({
27284             tooltip: this.nextText,
27285             cls: "next btn-outline-secondary",
27286             html : ' <i class="fa fa-forward"></i>',
27287             disabled: true,
27288             preventDefault: true,
27289             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27290         });
27291         this.last = this.navgroup.addItem({
27292             tooltip: this.lastText,
27293             html : ' <i class="fa fa-step-forward"></i>',
27294             cls: "next btn-outline-secondary",
27295             disabled: true,
27296             preventDefault: true,
27297             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27298         });
27299     //this.addSeparator();
27300         this.loading = this.navgroup.addItem({
27301             tooltip: this.refreshText,
27302             cls: "btn-outline-secondary",
27303             html : ' <i class="fa fa-refresh"></i>',
27304             preventDefault: true,
27305             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27306         });
27307         
27308     },
27309
27310     // private
27311     updateInfo : function(){
27312         if(this.displayEl){
27313             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27314             var msg = count == 0 ?
27315                 this.emptyMsg :
27316                 String.format(
27317                     this.displayMsg,
27318                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27319                 );
27320             this.displayEl.update(msg);
27321         }
27322     },
27323
27324     // private
27325     onLoad : function(ds, r, o)
27326     {
27327         this.cursor = o.params && o.params.start ? o.params.start : 0;
27328         
27329         var d = this.getPageData(),
27330             ap = d.activePage,
27331             ps = d.pages;
27332         
27333         
27334         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27335         this.field.dom.value = ap;
27336         this.first.setDisabled(ap == 1);
27337         this.prev.setDisabled(ap == 1);
27338         this.next.setDisabled(ap == ps);
27339         this.last.setDisabled(ap == ps);
27340         this.loading.enable();
27341         this.updateInfo();
27342     },
27343
27344     // private
27345     getPageData : function(){
27346         var total = this.ds.getTotalCount();
27347         return {
27348             total : total,
27349             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27350             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27351         };
27352     },
27353
27354     // private
27355     onLoadError : function(){
27356         this.loading.enable();
27357     },
27358
27359     // private
27360     onPagingKeydown : function(e){
27361         var k = e.getKey();
27362         var d = this.getPageData();
27363         if(k == e.RETURN){
27364             var v = this.field.dom.value, pageNum;
27365             if(!v || isNaN(pageNum = parseInt(v, 10))){
27366                 this.field.dom.value = d.activePage;
27367                 return;
27368             }
27369             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27370             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27371             e.stopEvent();
27372         }
27373         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))
27374         {
27375           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27376           this.field.dom.value = pageNum;
27377           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27378           e.stopEvent();
27379         }
27380         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27381         {
27382           var v = this.field.dom.value, pageNum; 
27383           var increment = (e.shiftKey) ? 10 : 1;
27384           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27385                 increment *= -1;
27386           }
27387           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27388             this.field.dom.value = d.activePage;
27389             return;
27390           }
27391           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27392           {
27393             this.field.dom.value = parseInt(v, 10) + increment;
27394             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27395             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27396           }
27397           e.stopEvent();
27398         }
27399     },
27400
27401     // private
27402     beforeLoad : function(){
27403         if(this.loading){
27404             this.loading.disable();
27405         }
27406     },
27407
27408     // private
27409     onClick : function(which){
27410         
27411         var ds = this.ds;
27412         if (!ds) {
27413             return;
27414         }
27415         
27416         switch(which){
27417             case "first":
27418                 ds.load({params:{start: 0, limit: this.pageSize}});
27419             break;
27420             case "prev":
27421                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27422             break;
27423             case "next":
27424                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27425             break;
27426             case "last":
27427                 var total = ds.getTotalCount();
27428                 var extra = total % this.pageSize;
27429                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27430                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27431             break;
27432             case "refresh":
27433                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27434             break;
27435         }
27436     },
27437
27438     /**
27439      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27440      * @param {Roo.data.Store} store The data store to unbind
27441      */
27442     unbind : function(ds){
27443         ds.un("beforeload", this.beforeLoad, this);
27444         ds.un("load", this.onLoad, this);
27445         ds.un("loadexception", this.onLoadError, this);
27446         ds.un("remove", this.updateInfo, this);
27447         ds.un("add", this.updateInfo, this);
27448         this.ds = undefined;
27449     },
27450
27451     /**
27452      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27453      * @param {Roo.data.Store} store The data store to bind
27454      */
27455     bind : function(ds){
27456         ds.on("beforeload", this.beforeLoad, this);
27457         ds.on("load", this.onLoad, this);
27458         ds.on("loadexception", this.onLoadError, this);
27459         ds.on("remove", this.updateInfo, this);
27460         ds.on("add", this.updateInfo, this);
27461         this.ds = ds;
27462     }
27463 });/*
27464  * - LGPL
27465  *
27466  * element
27467  * 
27468  */
27469
27470 /**
27471  * @class Roo.bootstrap.MessageBar
27472  * @extends Roo.bootstrap.Component
27473  * Bootstrap MessageBar class
27474  * @cfg {String} html contents of the MessageBar
27475  * @cfg {String} weight (info | success | warning | danger) default info
27476  * @cfg {String} beforeClass insert the bar before the given class
27477  * @cfg {Boolean} closable (true | false) default false
27478  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27479  * 
27480  * @constructor
27481  * Create a new Element
27482  * @param {Object} config The config object
27483  */
27484
27485 Roo.bootstrap.MessageBar = function(config){
27486     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27487 };
27488
27489 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27490     
27491     html: '',
27492     weight: 'info',
27493     closable: false,
27494     fixed: false,
27495     beforeClass: 'bootstrap-sticky-wrap',
27496     
27497     getAutoCreate : function(){
27498         
27499         var cfg = {
27500             tag: 'div',
27501             cls: 'alert alert-dismissable alert-' + this.weight,
27502             cn: [
27503                 {
27504                     tag: 'span',
27505                     cls: 'message',
27506                     html: this.html || ''
27507                 }
27508             ]
27509         };
27510         
27511         if(this.fixed){
27512             cfg.cls += ' alert-messages-fixed';
27513         }
27514         
27515         if(this.closable){
27516             cfg.cn.push({
27517                 tag: 'button',
27518                 cls: 'close',
27519                 html: 'x'
27520             });
27521         }
27522         
27523         return cfg;
27524     },
27525     
27526     onRender : function(ct, position)
27527     {
27528         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27529         
27530         if(!this.el){
27531             var cfg = Roo.apply({},  this.getAutoCreate());
27532             cfg.id = Roo.id();
27533             
27534             if (this.cls) {
27535                 cfg.cls += ' ' + this.cls;
27536             }
27537             if (this.style) {
27538                 cfg.style = this.style;
27539             }
27540             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27541             
27542             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27543         }
27544         
27545         this.el.select('>button.close').on('click', this.hide, this);
27546         
27547     },
27548     
27549     show : function()
27550     {
27551         if (!this.rendered) {
27552             this.render();
27553         }
27554         
27555         this.el.show();
27556         
27557         this.fireEvent('show', this);
27558         
27559     },
27560     
27561     hide : function()
27562     {
27563         if (!this.rendered) {
27564             this.render();
27565         }
27566         
27567         this.el.hide();
27568         
27569         this.fireEvent('hide', this);
27570     },
27571     
27572     update : function()
27573     {
27574 //        var e = this.el.dom.firstChild;
27575 //        
27576 //        if(this.closable){
27577 //            e = e.nextSibling;
27578 //        }
27579 //        
27580 //        e.data = this.html || '';
27581
27582         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27583     }
27584    
27585 });
27586
27587  
27588
27589      /*
27590  * - LGPL
27591  *
27592  * Graph
27593  * 
27594  */
27595
27596
27597 /**
27598  * @class Roo.bootstrap.Graph
27599  * @extends Roo.bootstrap.Component
27600  * Bootstrap Graph class
27601 > Prameters
27602  -sm {number} sm 4
27603  -md {number} md 5
27604  @cfg {String} graphtype  bar | vbar | pie
27605  @cfg {number} g_x coodinator | centre x (pie)
27606  @cfg {number} g_y coodinator | centre y (pie)
27607  @cfg {number} g_r radius (pie)
27608  @cfg {number} g_height height of the chart (respected by all elements in the set)
27609  @cfg {number} g_width width of the chart (respected by all elements in the set)
27610  @cfg {Object} title The title of the chart
27611     
27612  -{Array}  values
27613  -opts (object) options for the chart 
27614      o {
27615      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27616      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27617      o vgutter (number)
27618      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.
27619      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27620      o to
27621      o stretch (boolean)
27622      o }
27623  -opts (object) options for the pie
27624      o{
27625      o cut
27626      o startAngle (number)
27627      o endAngle (number)
27628      } 
27629  *
27630  * @constructor
27631  * Create a new Input
27632  * @param {Object} config The config object
27633  */
27634
27635 Roo.bootstrap.Graph = function(config){
27636     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27637     
27638     this.addEvents({
27639         // img events
27640         /**
27641          * @event click
27642          * The img click event for the img.
27643          * @param {Roo.EventObject} e
27644          */
27645         "click" : true
27646     });
27647 };
27648
27649 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27650     
27651     sm: 4,
27652     md: 5,
27653     graphtype: 'bar',
27654     g_height: 250,
27655     g_width: 400,
27656     g_x: 50,
27657     g_y: 50,
27658     g_r: 30,
27659     opts:{
27660         //g_colors: this.colors,
27661         g_type: 'soft',
27662         g_gutter: '20%'
27663
27664     },
27665     title : false,
27666
27667     getAutoCreate : function(){
27668         
27669         var cfg = {
27670             tag: 'div',
27671             html : null
27672         };
27673         
27674         
27675         return  cfg;
27676     },
27677
27678     onRender : function(ct,position){
27679         
27680         
27681         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27682         
27683         if (typeof(Raphael) == 'undefined') {
27684             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27685             return;
27686         }
27687         
27688         this.raphael = Raphael(this.el.dom);
27689         
27690                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27691                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27692                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27693                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27694                 /*
27695                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27696                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27697                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27698                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27699                 
27700                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27701                 r.barchart(330, 10, 300, 220, data1);
27702                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27703                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27704                 */
27705                 
27706                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27707                 // r.barchart(30, 30, 560, 250,  xdata, {
27708                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27709                 //     axis : "0 0 1 1",
27710                 //     axisxlabels :  xdata
27711                 //     //yvalues : cols,
27712                    
27713                 // });
27714 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27715 //        
27716 //        this.load(null,xdata,{
27717 //                axis : "0 0 1 1",
27718 //                axisxlabels :  xdata
27719 //                });
27720
27721     },
27722
27723     load : function(graphtype,xdata,opts)
27724     {
27725         this.raphael.clear();
27726         if(!graphtype) {
27727             graphtype = this.graphtype;
27728         }
27729         if(!opts){
27730             opts = this.opts;
27731         }
27732         var r = this.raphael,
27733             fin = function () {
27734                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27735             },
27736             fout = function () {
27737                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27738             },
27739             pfin = function() {
27740                 this.sector.stop();
27741                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27742
27743                 if (this.label) {
27744                     this.label[0].stop();
27745                     this.label[0].attr({ r: 7.5 });
27746                     this.label[1].attr({ "font-weight": 800 });
27747                 }
27748             },
27749             pfout = function() {
27750                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27751
27752                 if (this.label) {
27753                     this.label[0].animate({ r: 5 }, 500, "bounce");
27754                     this.label[1].attr({ "font-weight": 400 });
27755                 }
27756             };
27757
27758         switch(graphtype){
27759             case 'bar':
27760                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27761                 break;
27762             case 'hbar':
27763                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27764                 break;
27765             case 'pie':
27766 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27767 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27768 //            
27769                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27770                 
27771                 break;
27772
27773         }
27774         
27775         if(this.title){
27776             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27777         }
27778         
27779     },
27780     
27781     setTitle: function(o)
27782     {
27783         this.title = o;
27784     },
27785     
27786     initEvents: function() {
27787         
27788         if(!this.href){
27789             this.el.on('click', this.onClick, this);
27790         }
27791     },
27792     
27793     onClick : function(e)
27794     {
27795         Roo.log('img onclick');
27796         this.fireEvent('click', this, e);
27797     }
27798    
27799 });
27800
27801  
27802 /*
27803  * - LGPL
27804  *
27805  * numberBox
27806  * 
27807  */
27808 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27809
27810 /**
27811  * @class Roo.bootstrap.dash.NumberBox
27812  * @extends Roo.bootstrap.Component
27813  * Bootstrap NumberBox class
27814  * @cfg {String} headline Box headline
27815  * @cfg {String} content Box content
27816  * @cfg {String} icon Box icon
27817  * @cfg {String} footer Footer text
27818  * @cfg {String} fhref Footer href
27819  * 
27820  * @constructor
27821  * Create a new NumberBox
27822  * @param {Object} config The config object
27823  */
27824
27825
27826 Roo.bootstrap.dash.NumberBox = function(config){
27827     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27828     
27829 };
27830
27831 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27832     
27833     headline : '',
27834     content : '',
27835     icon : '',
27836     footer : '',
27837     fhref : '',
27838     ficon : '',
27839     
27840     getAutoCreate : function(){
27841         
27842         var cfg = {
27843             tag : 'div',
27844             cls : 'small-box ',
27845             cn : [
27846                 {
27847                     tag : 'div',
27848                     cls : 'inner',
27849                     cn :[
27850                         {
27851                             tag : 'h3',
27852                             cls : 'roo-headline',
27853                             html : this.headline
27854                         },
27855                         {
27856                             tag : 'p',
27857                             cls : 'roo-content',
27858                             html : this.content
27859                         }
27860                     ]
27861                 }
27862             ]
27863         };
27864         
27865         if(this.icon){
27866             cfg.cn.push({
27867                 tag : 'div',
27868                 cls : 'icon',
27869                 cn :[
27870                     {
27871                         tag : 'i',
27872                         cls : 'ion ' + this.icon
27873                     }
27874                 ]
27875             });
27876         }
27877         
27878         if(this.footer){
27879             var footer = {
27880                 tag : 'a',
27881                 cls : 'small-box-footer',
27882                 href : this.fhref || '#',
27883                 html : this.footer
27884             };
27885             
27886             cfg.cn.push(footer);
27887             
27888         }
27889         
27890         return  cfg;
27891     },
27892
27893     onRender : function(ct,position){
27894         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27895
27896
27897        
27898                 
27899     },
27900
27901     setHeadline: function (value)
27902     {
27903         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27904     },
27905     
27906     setFooter: function (value, href)
27907     {
27908         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27909         
27910         if(href){
27911             this.el.select('a.small-box-footer',true).first().attr('href', href);
27912         }
27913         
27914     },
27915
27916     setContent: function (value)
27917     {
27918         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27919     },
27920
27921     initEvents: function() 
27922     {   
27923         
27924     }
27925     
27926 });
27927
27928  
27929 /*
27930  * - LGPL
27931  *
27932  * TabBox
27933  * 
27934  */
27935 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27936
27937 /**
27938  * @class Roo.bootstrap.dash.TabBox
27939  * @extends Roo.bootstrap.Component
27940  * Bootstrap TabBox class
27941  * @cfg {String} title Title of the TabBox
27942  * @cfg {String} icon Icon of the TabBox
27943  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27944  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27945  * 
27946  * @constructor
27947  * Create a new TabBox
27948  * @param {Object} config The config object
27949  */
27950
27951
27952 Roo.bootstrap.dash.TabBox = function(config){
27953     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27954     this.addEvents({
27955         // raw events
27956         /**
27957          * @event addpane
27958          * When a pane is added
27959          * @param {Roo.bootstrap.dash.TabPane} pane
27960          */
27961         "addpane" : true,
27962         /**
27963          * @event activatepane
27964          * When a pane is activated
27965          * @param {Roo.bootstrap.dash.TabPane} pane
27966          */
27967         "activatepane" : true
27968         
27969          
27970     });
27971     
27972     this.panes = [];
27973 };
27974
27975 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27976
27977     title : '',
27978     icon : false,
27979     showtabs : true,
27980     tabScrollable : false,
27981     
27982     getChildContainer : function()
27983     {
27984         return this.el.select('.tab-content', true).first();
27985     },
27986     
27987     getAutoCreate : function(){
27988         
27989         var header = {
27990             tag: 'li',
27991             cls: 'pull-left header',
27992             html: this.title,
27993             cn : []
27994         };
27995         
27996         if(this.icon){
27997             header.cn.push({
27998                 tag: 'i',
27999                 cls: 'fa ' + this.icon
28000             });
28001         }
28002         
28003         var h = {
28004             tag: 'ul',
28005             cls: 'nav nav-tabs pull-right',
28006             cn: [
28007                 header
28008             ]
28009         };
28010         
28011         if(this.tabScrollable){
28012             h = {
28013                 tag: 'div',
28014                 cls: 'tab-header',
28015                 cn: [
28016                     {
28017                         tag: 'ul',
28018                         cls: 'nav nav-tabs pull-right',
28019                         cn: [
28020                             header
28021                         ]
28022                     }
28023                 ]
28024             };
28025         }
28026         
28027         var cfg = {
28028             tag: 'div',
28029             cls: 'nav-tabs-custom',
28030             cn: [
28031                 h,
28032                 {
28033                     tag: 'div',
28034                     cls: 'tab-content no-padding',
28035                     cn: []
28036                 }
28037             ]
28038         };
28039
28040         return  cfg;
28041     },
28042     initEvents : function()
28043     {
28044         //Roo.log('add add pane handler');
28045         this.on('addpane', this.onAddPane, this);
28046     },
28047      /**
28048      * Updates the box title
28049      * @param {String} html to set the title to.
28050      */
28051     setTitle : function(value)
28052     {
28053         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28054     },
28055     onAddPane : function(pane)
28056     {
28057         this.panes.push(pane);
28058         //Roo.log('addpane');
28059         //Roo.log(pane);
28060         // tabs are rendere left to right..
28061         if(!this.showtabs){
28062             return;
28063         }
28064         
28065         var ctr = this.el.select('.nav-tabs', true).first();
28066          
28067          
28068         var existing = ctr.select('.nav-tab',true);
28069         var qty = existing.getCount();;
28070         
28071         
28072         var tab = ctr.createChild({
28073             tag : 'li',
28074             cls : 'nav-tab' + (qty ? '' : ' active'),
28075             cn : [
28076                 {
28077                     tag : 'a',
28078                     href:'#',
28079                     html : pane.title
28080                 }
28081             ]
28082         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28083         pane.tab = tab;
28084         
28085         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28086         if (!qty) {
28087             pane.el.addClass('active');
28088         }
28089         
28090                 
28091     },
28092     onTabClick : function(ev,un,ob,pane)
28093     {
28094         //Roo.log('tab - prev default');
28095         ev.preventDefault();
28096         
28097         
28098         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28099         pane.tab.addClass('active');
28100         //Roo.log(pane.title);
28101         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28102         // technically we should have a deactivate event.. but maybe add later.
28103         // and it should not de-activate the selected tab...
28104         this.fireEvent('activatepane', pane);
28105         pane.el.addClass('active');
28106         pane.fireEvent('activate');
28107         
28108         
28109     },
28110     
28111     getActivePane : function()
28112     {
28113         var r = false;
28114         Roo.each(this.panes, function(p) {
28115             if(p.el.hasClass('active')){
28116                 r = p;
28117                 return false;
28118             }
28119             
28120             return;
28121         });
28122         
28123         return r;
28124     }
28125     
28126     
28127 });
28128
28129  
28130 /*
28131  * - LGPL
28132  *
28133  * Tab pane
28134  * 
28135  */
28136 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28137 /**
28138  * @class Roo.bootstrap.TabPane
28139  * @extends Roo.bootstrap.Component
28140  * Bootstrap TabPane class
28141  * @cfg {Boolean} active (false | true) Default false
28142  * @cfg {String} title title of panel
28143
28144  * 
28145  * @constructor
28146  * Create a new TabPane
28147  * @param {Object} config The config object
28148  */
28149
28150 Roo.bootstrap.dash.TabPane = function(config){
28151     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28152     
28153     this.addEvents({
28154         // raw events
28155         /**
28156          * @event activate
28157          * When a pane is activated
28158          * @param {Roo.bootstrap.dash.TabPane} pane
28159          */
28160         "activate" : true
28161          
28162     });
28163 };
28164
28165 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28166     
28167     active : false,
28168     title : '',
28169     
28170     // the tabBox that this is attached to.
28171     tab : false,
28172      
28173     getAutoCreate : function() 
28174     {
28175         var cfg = {
28176             tag: 'div',
28177             cls: 'tab-pane'
28178         };
28179         
28180         if(this.active){
28181             cfg.cls += ' active';
28182         }
28183         
28184         return cfg;
28185     },
28186     initEvents  : function()
28187     {
28188         //Roo.log('trigger add pane handler');
28189         this.parent().fireEvent('addpane', this)
28190     },
28191     
28192      /**
28193      * Updates the tab title 
28194      * @param {String} html to set the title to.
28195      */
28196     setTitle: function(str)
28197     {
28198         if (!this.tab) {
28199             return;
28200         }
28201         this.title = str;
28202         this.tab.select('a', true).first().dom.innerHTML = str;
28203         
28204     }
28205     
28206     
28207     
28208 });
28209
28210  
28211
28212
28213  /*
28214  * - LGPL
28215  *
28216  * menu
28217  * 
28218  */
28219 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28220
28221 /**
28222  * @class Roo.bootstrap.menu.Menu
28223  * @extends Roo.bootstrap.Component
28224  * Bootstrap Menu class - container for Menu
28225  * @cfg {String} html Text of the menu
28226  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28227  * @cfg {String} icon Font awesome icon
28228  * @cfg {String} pos Menu align to (top | bottom) default bottom
28229  * 
28230  * 
28231  * @constructor
28232  * Create a new Menu
28233  * @param {Object} config The config object
28234  */
28235
28236
28237 Roo.bootstrap.menu.Menu = function(config){
28238     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28239     
28240     this.addEvents({
28241         /**
28242          * @event beforeshow
28243          * Fires before this menu is displayed
28244          * @param {Roo.bootstrap.menu.Menu} this
28245          */
28246         beforeshow : true,
28247         /**
28248          * @event beforehide
28249          * Fires before this menu is hidden
28250          * @param {Roo.bootstrap.menu.Menu} this
28251          */
28252         beforehide : true,
28253         /**
28254          * @event show
28255          * Fires after this menu is displayed
28256          * @param {Roo.bootstrap.menu.Menu} this
28257          */
28258         show : true,
28259         /**
28260          * @event hide
28261          * Fires after this menu is hidden
28262          * @param {Roo.bootstrap.menu.Menu} this
28263          */
28264         hide : true,
28265         /**
28266          * @event click
28267          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28268          * @param {Roo.bootstrap.menu.Menu} this
28269          * @param {Roo.EventObject} e
28270          */
28271         click : true
28272     });
28273     
28274 };
28275
28276 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28277     
28278     submenu : false,
28279     html : '',
28280     weight : 'default',
28281     icon : false,
28282     pos : 'bottom',
28283     
28284     
28285     getChildContainer : function() {
28286         if(this.isSubMenu){
28287             return this.el;
28288         }
28289         
28290         return this.el.select('ul.dropdown-menu', true).first();  
28291     },
28292     
28293     getAutoCreate : function()
28294     {
28295         var text = [
28296             {
28297                 tag : 'span',
28298                 cls : 'roo-menu-text',
28299                 html : this.html
28300             }
28301         ];
28302         
28303         if(this.icon){
28304             text.unshift({
28305                 tag : 'i',
28306                 cls : 'fa ' + this.icon
28307             })
28308         }
28309         
28310         
28311         var cfg = {
28312             tag : 'div',
28313             cls : 'btn-group',
28314             cn : [
28315                 {
28316                     tag : 'button',
28317                     cls : 'dropdown-button btn btn-' + this.weight,
28318                     cn : text
28319                 },
28320                 {
28321                     tag : 'button',
28322                     cls : 'dropdown-toggle btn btn-' + this.weight,
28323                     cn : [
28324                         {
28325                             tag : 'span',
28326                             cls : 'caret'
28327                         }
28328                     ]
28329                 },
28330                 {
28331                     tag : 'ul',
28332                     cls : 'dropdown-menu'
28333                 }
28334             ]
28335             
28336         };
28337         
28338         if(this.pos == 'top'){
28339             cfg.cls += ' dropup';
28340         }
28341         
28342         if(this.isSubMenu){
28343             cfg = {
28344                 tag : 'ul',
28345                 cls : 'dropdown-menu'
28346             }
28347         }
28348         
28349         return cfg;
28350     },
28351     
28352     onRender : function(ct, position)
28353     {
28354         this.isSubMenu = ct.hasClass('dropdown-submenu');
28355         
28356         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28357     },
28358     
28359     initEvents : function() 
28360     {
28361         if(this.isSubMenu){
28362             return;
28363         }
28364         
28365         this.hidden = true;
28366         
28367         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28368         this.triggerEl.on('click', this.onTriggerPress, this);
28369         
28370         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28371         this.buttonEl.on('click', this.onClick, this);
28372         
28373     },
28374     
28375     list : function()
28376     {
28377         if(this.isSubMenu){
28378             return this.el;
28379         }
28380         
28381         return this.el.select('ul.dropdown-menu', true).first();
28382     },
28383     
28384     onClick : function(e)
28385     {
28386         this.fireEvent("click", this, e);
28387     },
28388     
28389     onTriggerPress  : function(e)
28390     {   
28391         if (this.isVisible()) {
28392             this.hide();
28393         } else {
28394             this.show();
28395         }
28396     },
28397     
28398     isVisible : function(){
28399         return !this.hidden;
28400     },
28401     
28402     show : function()
28403     {
28404         this.fireEvent("beforeshow", this);
28405         
28406         this.hidden = false;
28407         this.el.addClass('open');
28408         
28409         Roo.get(document).on("mouseup", this.onMouseUp, this);
28410         
28411         this.fireEvent("show", this);
28412         
28413         
28414     },
28415     
28416     hide : function()
28417     {
28418         this.fireEvent("beforehide", this);
28419         
28420         this.hidden = true;
28421         this.el.removeClass('open');
28422         
28423         Roo.get(document).un("mouseup", this.onMouseUp);
28424         
28425         this.fireEvent("hide", this);
28426     },
28427     
28428     onMouseUp : function()
28429     {
28430         this.hide();
28431     }
28432     
28433 });
28434
28435  
28436  /*
28437  * - LGPL
28438  *
28439  * menu item
28440  * 
28441  */
28442 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28443
28444 /**
28445  * @class Roo.bootstrap.menu.Item
28446  * @extends Roo.bootstrap.Component
28447  * Bootstrap MenuItem class
28448  * @cfg {Boolean} submenu (true | false) default false
28449  * @cfg {String} html text of the item
28450  * @cfg {String} href the link
28451  * @cfg {Boolean} disable (true | false) default false
28452  * @cfg {Boolean} preventDefault (true | false) default true
28453  * @cfg {String} icon Font awesome icon
28454  * @cfg {String} pos Submenu align to (left | right) default right 
28455  * 
28456  * 
28457  * @constructor
28458  * Create a new Item
28459  * @param {Object} config The config object
28460  */
28461
28462
28463 Roo.bootstrap.menu.Item = function(config){
28464     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28465     this.addEvents({
28466         /**
28467          * @event mouseover
28468          * Fires when the mouse is hovering over this menu
28469          * @param {Roo.bootstrap.menu.Item} this
28470          * @param {Roo.EventObject} e
28471          */
28472         mouseover : true,
28473         /**
28474          * @event mouseout
28475          * Fires when the mouse exits this menu
28476          * @param {Roo.bootstrap.menu.Item} this
28477          * @param {Roo.EventObject} e
28478          */
28479         mouseout : true,
28480         // raw events
28481         /**
28482          * @event click
28483          * The raw click event for the entire grid.
28484          * @param {Roo.EventObject} e
28485          */
28486         click : true
28487     });
28488 };
28489
28490 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28491     
28492     submenu : false,
28493     href : '',
28494     html : '',
28495     preventDefault: true,
28496     disable : false,
28497     icon : false,
28498     pos : 'right',
28499     
28500     getAutoCreate : function()
28501     {
28502         var text = [
28503             {
28504                 tag : 'span',
28505                 cls : 'roo-menu-item-text',
28506                 html : this.html
28507             }
28508         ];
28509         
28510         if(this.icon){
28511             text.unshift({
28512                 tag : 'i',
28513                 cls : 'fa ' + this.icon
28514             })
28515         }
28516         
28517         var cfg = {
28518             tag : 'li',
28519             cn : [
28520                 {
28521                     tag : 'a',
28522                     href : this.href || '#',
28523                     cn : text
28524                 }
28525             ]
28526         };
28527         
28528         if(this.disable){
28529             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28530         }
28531         
28532         if(this.submenu){
28533             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28534             
28535             if(this.pos == 'left'){
28536                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28537             }
28538         }
28539         
28540         return cfg;
28541     },
28542     
28543     initEvents : function() 
28544     {
28545         this.el.on('mouseover', this.onMouseOver, this);
28546         this.el.on('mouseout', this.onMouseOut, this);
28547         
28548         this.el.select('a', true).first().on('click', this.onClick, this);
28549         
28550     },
28551     
28552     onClick : function(e)
28553     {
28554         if(this.preventDefault){
28555             e.preventDefault();
28556         }
28557         
28558         this.fireEvent("click", this, e);
28559     },
28560     
28561     onMouseOver : function(e)
28562     {
28563         if(this.submenu && this.pos == 'left'){
28564             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28565         }
28566         
28567         this.fireEvent("mouseover", this, e);
28568     },
28569     
28570     onMouseOut : function(e)
28571     {
28572         this.fireEvent("mouseout", this, e);
28573     }
28574 });
28575
28576  
28577
28578  /*
28579  * - LGPL
28580  *
28581  * menu separator
28582  * 
28583  */
28584 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28585
28586 /**
28587  * @class Roo.bootstrap.menu.Separator
28588  * @extends Roo.bootstrap.Component
28589  * Bootstrap Separator class
28590  * 
28591  * @constructor
28592  * Create a new Separator
28593  * @param {Object} config The config object
28594  */
28595
28596
28597 Roo.bootstrap.menu.Separator = function(config){
28598     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28599 };
28600
28601 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28602     
28603     getAutoCreate : function(){
28604         var cfg = {
28605             tag : 'li',
28606             cls: 'divider'
28607         };
28608         
28609         return cfg;
28610     }
28611    
28612 });
28613
28614  
28615
28616  /*
28617  * - LGPL
28618  *
28619  * Tooltip
28620  * 
28621  */
28622
28623 /**
28624  * @class Roo.bootstrap.Tooltip
28625  * Bootstrap Tooltip class
28626  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28627  * to determine which dom element triggers the tooltip.
28628  * 
28629  * It needs to add support for additional attributes like tooltip-position
28630  * 
28631  * @constructor
28632  * Create a new Toolti
28633  * @param {Object} config The config object
28634  */
28635
28636 Roo.bootstrap.Tooltip = function(config){
28637     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28638     
28639     this.alignment = Roo.bootstrap.Tooltip.alignment;
28640     
28641     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28642         this.alignment = config.alignment;
28643     }
28644     
28645 };
28646
28647 Roo.apply(Roo.bootstrap.Tooltip, {
28648     /**
28649      * @function init initialize tooltip monitoring.
28650      * @static
28651      */
28652     currentEl : false,
28653     currentTip : false,
28654     currentRegion : false,
28655     
28656     //  init : delay?
28657     
28658     init : function()
28659     {
28660         Roo.get(document).on('mouseover', this.enter ,this);
28661         Roo.get(document).on('mouseout', this.leave, this);
28662          
28663         
28664         this.currentTip = new Roo.bootstrap.Tooltip();
28665     },
28666     
28667     enter : function(ev)
28668     {
28669         var dom = ev.getTarget();
28670         
28671         //Roo.log(['enter',dom]);
28672         var el = Roo.fly(dom);
28673         if (this.currentEl) {
28674             //Roo.log(dom);
28675             //Roo.log(this.currentEl);
28676             //Roo.log(this.currentEl.contains(dom));
28677             if (this.currentEl == el) {
28678                 return;
28679             }
28680             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28681                 return;
28682             }
28683
28684         }
28685         
28686         if (this.currentTip.el) {
28687             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28688         }    
28689         //Roo.log(ev);
28690         
28691         if(!el || el.dom == document){
28692             return;
28693         }
28694         
28695         var bindEl = el;
28696         
28697         // you can not look for children, as if el is the body.. then everythign is the child..
28698         if (!el.attr('tooltip')) { //
28699             if (!el.select("[tooltip]").elements.length) {
28700                 return;
28701             }
28702             // is the mouse over this child...?
28703             bindEl = el.select("[tooltip]").first();
28704             var xy = ev.getXY();
28705             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28706                 //Roo.log("not in region.");
28707                 return;
28708             }
28709             //Roo.log("child element over..");
28710             
28711         }
28712         this.currentEl = bindEl;
28713         this.currentTip.bind(bindEl);
28714         this.currentRegion = Roo.lib.Region.getRegion(dom);
28715         this.currentTip.enter();
28716         
28717     },
28718     leave : function(ev)
28719     {
28720         var dom = ev.getTarget();
28721         //Roo.log(['leave',dom]);
28722         if (!this.currentEl) {
28723             return;
28724         }
28725         
28726         
28727         if (dom != this.currentEl.dom) {
28728             return;
28729         }
28730         var xy = ev.getXY();
28731         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28732             return;
28733         }
28734         // only activate leave if mouse cursor is outside... bounding box..
28735         
28736         
28737         
28738         
28739         if (this.currentTip) {
28740             this.currentTip.leave();
28741         }
28742         //Roo.log('clear currentEl');
28743         this.currentEl = false;
28744         
28745         
28746     },
28747     alignment : {
28748         'left' : ['r-l', [-2,0], 'right'],
28749         'right' : ['l-r', [2,0], 'left'],
28750         'bottom' : ['t-b', [0,2], 'top'],
28751         'top' : [ 'b-t', [0,-2], 'bottom']
28752     }
28753     
28754 });
28755
28756
28757 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28758     
28759     
28760     bindEl : false,
28761     
28762     delay : null, // can be { show : 300 , hide: 500}
28763     
28764     timeout : null,
28765     
28766     hoverState : null, //???
28767     
28768     placement : 'bottom', 
28769     
28770     alignment : false,
28771     
28772     getAutoCreate : function(){
28773     
28774         var cfg = {
28775            cls : 'tooltip',   
28776            role : 'tooltip',
28777            cn : [
28778                 {
28779                     cls : 'tooltip-arrow arrow'
28780                 },
28781                 {
28782                     cls : 'tooltip-inner'
28783                 }
28784            ]
28785         };
28786         
28787         return cfg;
28788     },
28789     bind : function(el)
28790     {
28791         this.bindEl = el;
28792     },
28793     
28794     initEvents : function()
28795     {
28796         this.arrowEl = this.el.select('.arrow', true).first();
28797         this.innerEl = this.el.select('.tooltip-inner', true).first();
28798     },
28799     
28800     enter : function () {
28801        
28802         if (this.timeout != null) {
28803             clearTimeout(this.timeout);
28804         }
28805         
28806         this.hoverState = 'in';
28807          //Roo.log("enter - show");
28808         if (!this.delay || !this.delay.show) {
28809             this.show();
28810             return;
28811         }
28812         var _t = this;
28813         this.timeout = setTimeout(function () {
28814             if (_t.hoverState == 'in') {
28815                 _t.show();
28816             }
28817         }, this.delay.show);
28818     },
28819     leave : function()
28820     {
28821         clearTimeout(this.timeout);
28822     
28823         this.hoverState = 'out';
28824          if (!this.delay || !this.delay.hide) {
28825             this.hide();
28826             return;
28827         }
28828        
28829         var _t = this;
28830         this.timeout = setTimeout(function () {
28831             //Roo.log("leave - timeout");
28832             
28833             if (_t.hoverState == 'out') {
28834                 _t.hide();
28835                 Roo.bootstrap.Tooltip.currentEl = false;
28836             }
28837         }, delay);
28838     },
28839     
28840     show : function (msg)
28841     {
28842         if (!this.el) {
28843             this.render(document.body);
28844         }
28845         // set content.
28846         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28847         
28848         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28849         
28850         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28851         
28852         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28853                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28854         
28855         var placement = typeof this.placement == 'function' ?
28856             this.placement.call(this, this.el, on_el) :
28857             this.placement;
28858             
28859         var autoToken = /\s?auto?\s?/i;
28860         var autoPlace = autoToken.test(placement);
28861         if (autoPlace) {
28862             placement = placement.replace(autoToken, '') || 'top';
28863         }
28864         
28865         //this.el.detach()
28866         //this.el.setXY([0,0]);
28867         this.el.show();
28868         //this.el.dom.style.display='block';
28869         
28870         //this.el.appendTo(on_el);
28871         
28872         var p = this.getPosition();
28873         var box = this.el.getBox();
28874         
28875         if (autoPlace) {
28876             // fixme..
28877         }
28878         
28879         var align = this.alignment[placement];
28880         
28881         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28882         
28883         if(placement == 'top' || placement == 'bottom'){
28884             if(xy[0] < 0){
28885                 placement = 'right';
28886             }
28887             
28888             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28889                 placement = 'left';
28890             }
28891             
28892             var scroll = Roo.select('body', true).first().getScroll();
28893             
28894             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28895                 placement = 'top';
28896             }
28897             
28898             align = this.alignment[placement];
28899             
28900             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28901             
28902         }
28903         
28904         this.el.alignTo(this.bindEl, align[0],align[1]);
28905         //var arrow = this.el.select('.arrow',true).first();
28906         //arrow.set(align[2], 
28907         
28908         this.el.addClass(placement);
28909         this.el.addClass("bs-tooltip-"+ placement);
28910         
28911         this.el.addClass('in fade show');
28912         
28913         this.hoverState = null;
28914         
28915         if (this.el.hasClass('fade')) {
28916             // fade it?
28917         }
28918         
28919         
28920         
28921         
28922         
28923     },
28924     hide : function()
28925     {
28926          
28927         if (!this.el) {
28928             return;
28929         }
28930         //this.el.setXY([0,0]);
28931         this.el.removeClass(['show', 'in']);
28932         //this.el.hide();
28933         
28934     }
28935     
28936 });
28937  
28938
28939  /*
28940  * - LGPL
28941  *
28942  * Location Picker
28943  * 
28944  */
28945
28946 /**
28947  * @class Roo.bootstrap.LocationPicker
28948  * @extends Roo.bootstrap.Component
28949  * Bootstrap LocationPicker class
28950  * @cfg {Number} latitude Position when init default 0
28951  * @cfg {Number} longitude Position when init default 0
28952  * @cfg {Number} zoom default 15
28953  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28954  * @cfg {Boolean} mapTypeControl default false
28955  * @cfg {Boolean} disableDoubleClickZoom default false
28956  * @cfg {Boolean} scrollwheel default true
28957  * @cfg {Boolean} streetViewControl default false
28958  * @cfg {Number} radius default 0
28959  * @cfg {String} locationName
28960  * @cfg {Boolean} draggable default true
28961  * @cfg {Boolean} enableAutocomplete default false
28962  * @cfg {Boolean} enableReverseGeocode default true
28963  * @cfg {String} markerTitle
28964  * 
28965  * @constructor
28966  * Create a new LocationPicker
28967  * @param {Object} config The config object
28968  */
28969
28970
28971 Roo.bootstrap.LocationPicker = function(config){
28972     
28973     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28974     
28975     this.addEvents({
28976         /**
28977          * @event initial
28978          * Fires when the picker initialized.
28979          * @param {Roo.bootstrap.LocationPicker} this
28980          * @param {Google Location} location
28981          */
28982         initial : true,
28983         /**
28984          * @event positionchanged
28985          * Fires when the picker position changed.
28986          * @param {Roo.bootstrap.LocationPicker} this
28987          * @param {Google Location} location
28988          */
28989         positionchanged : true,
28990         /**
28991          * @event resize
28992          * Fires when the map resize.
28993          * @param {Roo.bootstrap.LocationPicker} this
28994          */
28995         resize : true,
28996         /**
28997          * @event show
28998          * Fires when the map show.
28999          * @param {Roo.bootstrap.LocationPicker} this
29000          */
29001         show : true,
29002         /**
29003          * @event hide
29004          * Fires when the map hide.
29005          * @param {Roo.bootstrap.LocationPicker} this
29006          */
29007         hide : true,
29008         /**
29009          * @event mapClick
29010          * Fires when click the map.
29011          * @param {Roo.bootstrap.LocationPicker} this
29012          * @param {Map event} e
29013          */
29014         mapClick : true,
29015         /**
29016          * @event mapRightClick
29017          * Fires when right click the map.
29018          * @param {Roo.bootstrap.LocationPicker} this
29019          * @param {Map event} e
29020          */
29021         mapRightClick : true,
29022         /**
29023          * @event markerClick
29024          * Fires when click the marker.
29025          * @param {Roo.bootstrap.LocationPicker} this
29026          * @param {Map event} e
29027          */
29028         markerClick : true,
29029         /**
29030          * @event markerRightClick
29031          * Fires when right click the marker.
29032          * @param {Roo.bootstrap.LocationPicker} this
29033          * @param {Map event} e
29034          */
29035         markerRightClick : true,
29036         /**
29037          * @event OverlayViewDraw
29038          * Fires when OverlayView Draw
29039          * @param {Roo.bootstrap.LocationPicker} this
29040          */
29041         OverlayViewDraw : true,
29042         /**
29043          * @event OverlayViewOnAdd
29044          * Fires when OverlayView Draw
29045          * @param {Roo.bootstrap.LocationPicker} this
29046          */
29047         OverlayViewOnAdd : true,
29048         /**
29049          * @event OverlayViewOnRemove
29050          * Fires when OverlayView Draw
29051          * @param {Roo.bootstrap.LocationPicker} this
29052          */
29053         OverlayViewOnRemove : true,
29054         /**
29055          * @event OverlayViewShow
29056          * Fires when OverlayView Draw
29057          * @param {Roo.bootstrap.LocationPicker} this
29058          * @param {Pixel} cpx
29059          */
29060         OverlayViewShow : true,
29061         /**
29062          * @event OverlayViewHide
29063          * Fires when OverlayView Draw
29064          * @param {Roo.bootstrap.LocationPicker} this
29065          */
29066         OverlayViewHide : true,
29067         /**
29068          * @event loadexception
29069          * Fires when load google lib failed.
29070          * @param {Roo.bootstrap.LocationPicker} this
29071          */
29072         loadexception : true
29073     });
29074         
29075 };
29076
29077 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29078     
29079     gMapContext: false,
29080     
29081     latitude: 0,
29082     longitude: 0,
29083     zoom: 15,
29084     mapTypeId: false,
29085     mapTypeControl: false,
29086     disableDoubleClickZoom: false,
29087     scrollwheel: true,
29088     streetViewControl: false,
29089     radius: 0,
29090     locationName: '',
29091     draggable: true,
29092     enableAutocomplete: false,
29093     enableReverseGeocode: true,
29094     markerTitle: '',
29095     
29096     getAutoCreate: function()
29097     {
29098
29099         var cfg = {
29100             tag: 'div',
29101             cls: 'roo-location-picker'
29102         };
29103         
29104         return cfg
29105     },
29106     
29107     initEvents: function(ct, position)
29108     {       
29109         if(!this.el.getWidth() || this.isApplied()){
29110             return;
29111         }
29112         
29113         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29114         
29115         this.initial();
29116     },
29117     
29118     initial: function()
29119     {
29120         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29121             this.fireEvent('loadexception', this);
29122             return;
29123         }
29124         
29125         if(!this.mapTypeId){
29126             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29127         }
29128         
29129         this.gMapContext = this.GMapContext();
29130         
29131         this.initOverlayView();
29132         
29133         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29134         
29135         var _this = this;
29136                 
29137         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29138             _this.setPosition(_this.gMapContext.marker.position);
29139         });
29140         
29141         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29142             _this.fireEvent('mapClick', this, event);
29143             
29144         });
29145
29146         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29147             _this.fireEvent('mapRightClick', this, event);
29148             
29149         });
29150         
29151         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29152             _this.fireEvent('markerClick', this, event);
29153             
29154         });
29155
29156         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29157             _this.fireEvent('markerRightClick', this, event);
29158             
29159         });
29160         
29161         this.setPosition(this.gMapContext.location);
29162         
29163         this.fireEvent('initial', this, this.gMapContext.location);
29164     },
29165     
29166     initOverlayView: function()
29167     {
29168         var _this = this;
29169         
29170         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29171             
29172             draw: function()
29173             {
29174                 _this.fireEvent('OverlayViewDraw', _this);
29175             },
29176             
29177             onAdd: function()
29178             {
29179                 _this.fireEvent('OverlayViewOnAdd', _this);
29180             },
29181             
29182             onRemove: function()
29183             {
29184                 _this.fireEvent('OverlayViewOnRemove', _this);
29185             },
29186             
29187             show: function(cpx)
29188             {
29189                 _this.fireEvent('OverlayViewShow', _this, cpx);
29190             },
29191             
29192             hide: function()
29193             {
29194                 _this.fireEvent('OverlayViewHide', _this);
29195             }
29196             
29197         });
29198     },
29199     
29200     fromLatLngToContainerPixel: function(event)
29201     {
29202         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29203     },
29204     
29205     isApplied: function() 
29206     {
29207         return this.getGmapContext() == false ? false : true;
29208     },
29209     
29210     getGmapContext: function() 
29211     {
29212         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29213     },
29214     
29215     GMapContext: function() 
29216     {
29217         var position = new google.maps.LatLng(this.latitude, this.longitude);
29218         
29219         var _map = new google.maps.Map(this.el.dom, {
29220             center: position,
29221             zoom: this.zoom,
29222             mapTypeId: this.mapTypeId,
29223             mapTypeControl: this.mapTypeControl,
29224             disableDoubleClickZoom: this.disableDoubleClickZoom,
29225             scrollwheel: this.scrollwheel,
29226             streetViewControl: this.streetViewControl,
29227             locationName: this.locationName,
29228             draggable: this.draggable,
29229             enableAutocomplete: this.enableAutocomplete,
29230             enableReverseGeocode: this.enableReverseGeocode
29231         });
29232         
29233         var _marker = new google.maps.Marker({
29234             position: position,
29235             map: _map,
29236             title: this.markerTitle,
29237             draggable: this.draggable
29238         });
29239         
29240         return {
29241             map: _map,
29242             marker: _marker,
29243             circle: null,
29244             location: position,
29245             radius: this.radius,
29246             locationName: this.locationName,
29247             addressComponents: {
29248                 formatted_address: null,
29249                 addressLine1: null,
29250                 addressLine2: null,
29251                 streetName: null,
29252                 streetNumber: null,
29253                 city: null,
29254                 district: null,
29255                 state: null,
29256                 stateOrProvince: null
29257             },
29258             settings: this,
29259             domContainer: this.el.dom,
29260             geodecoder: new google.maps.Geocoder()
29261         };
29262     },
29263     
29264     drawCircle: function(center, radius, options) 
29265     {
29266         if (this.gMapContext.circle != null) {
29267             this.gMapContext.circle.setMap(null);
29268         }
29269         if (radius > 0) {
29270             radius *= 1;
29271             options = Roo.apply({}, options, {
29272                 strokeColor: "#0000FF",
29273                 strokeOpacity: .35,
29274                 strokeWeight: 2,
29275                 fillColor: "#0000FF",
29276                 fillOpacity: .2
29277             });
29278             
29279             options.map = this.gMapContext.map;
29280             options.radius = radius;
29281             options.center = center;
29282             this.gMapContext.circle = new google.maps.Circle(options);
29283             return this.gMapContext.circle;
29284         }
29285         
29286         return null;
29287     },
29288     
29289     setPosition: function(location) 
29290     {
29291         this.gMapContext.location = location;
29292         this.gMapContext.marker.setPosition(location);
29293         this.gMapContext.map.panTo(location);
29294         this.drawCircle(location, this.gMapContext.radius, {});
29295         
29296         var _this = this;
29297         
29298         if (this.gMapContext.settings.enableReverseGeocode) {
29299             this.gMapContext.geodecoder.geocode({
29300                 latLng: this.gMapContext.location
29301             }, function(results, status) {
29302                 
29303                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29304                     _this.gMapContext.locationName = results[0].formatted_address;
29305                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29306                     
29307                     _this.fireEvent('positionchanged', this, location);
29308                 }
29309             });
29310             
29311             return;
29312         }
29313         
29314         this.fireEvent('positionchanged', this, location);
29315     },
29316     
29317     resize: function()
29318     {
29319         google.maps.event.trigger(this.gMapContext.map, "resize");
29320         
29321         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29322         
29323         this.fireEvent('resize', this);
29324     },
29325     
29326     setPositionByLatLng: function(latitude, longitude)
29327     {
29328         this.setPosition(new google.maps.LatLng(latitude, longitude));
29329     },
29330     
29331     getCurrentPosition: function() 
29332     {
29333         return {
29334             latitude: this.gMapContext.location.lat(),
29335             longitude: this.gMapContext.location.lng()
29336         };
29337     },
29338     
29339     getAddressName: function() 
29340     {
29341         return this.gMapContext.locationName;
29342     },
29343     
29344     getAddressComponents: function() 
29345     {
29346         return this.gMapContext.addressComponents;
29347     },
29348     
29349     address_component_from_google_geocode: function(address_components) 
29350     {
29351         var result = {};
29352         
29353         for (var i = 0; i < address_components.length; i++) {
29354             var component = address_components[i];
29355             if (component.types.indexOf("postal_code") >= 0) {
29356                 result.postalCode = component.short_name;
29357             } else if (component.types.indexOf("street_number") >= 0) {
29358                 result.streetNumber = component.short_name;
29359             } else if (component.types.indexOf("route") >= 0) {
29360                 result.streetName = component.short_name;
29361             } else if (component.types.indexOf("neighborhood") >= 0) {
29362                 result.city = component.short_name;
29363             } else if (component.types.indexOf("locality") >= 0) {
29364                 result.city = component.short_name;
29365             } else if (component.types.indexOf("sublocality") >= 0) {
29366                 result.district = component.short_name;
29367             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29368                 result.stateOrProvince = component.short_name;
29369             } else if (component.types.indexOf("country") >= 0) {
29370                 result.country = component.short_name;
29371             }
29372         }
29373         
29374         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29375         result.addressLine2 = "";
29376         return result;
29377     },
29378     
29379     setZoomLevel: function(zoom)
29380     {
29381         this.gMapContext.map.setZoom(zoom);
29382     },
29383     
29384     show: function()
29385     {
29386         if(!this.el){
29387             return;
29388         }
29389         
29390         this.el.show();
29391         
29392         this.resize();
29393         
29394         this.fireEvent('show', this);
29395     },
29396     
29397     hide: function()
29398     {
29399         if(!this.el){
29400             return;
29401         }
29402         
29403         this.el.hide();
29404         
29405         this.fireEvent('hide', this);
29406     }
29407     
29408 });
29409
29410 Roo.apply(Roo.bootstrap.LocationPicker, {
29411     
29412     OverlayView : function(map, options)
29413     {
29414         options = options || {};
29415         
29416         this.setMap(map);
29417     }
29418     
29419     
29420 });/**
29421  * @class Roo.bootstrap.Alert
29422  * @extends Roo.bootstrap.Component
29423  * Bootstrap Alert class - shows an alert area box
29424  * eg
29425  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29426   Enter a valid email address
29427 </div>
29428  * @licence LGPL
29429  * @cfg {String} title The title of alert
29430  * @cfg {String} html The content of alert
29431  * @cfg {String} weight (  success | info | warning | danger )
29432  * @cfg {String} faicon font-awesomeicon
29433  * 
29434  * @constructor
29435  * Create a new alert
29436  * @param {Object} config The config object
29437  */
29438
29439
29440 Roo.bootstrap.Alert = function(config){
29441     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29442     
29443 };
29444
29445 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29446     
29447     title: '',
29448     html: '',
29449     weight: false,
29450     faicon: false,
29451     
29452     getAutoCreate : function()
29453     {
29454         
29455         var cfg = {
29456             tag : 'div',
29457             cls : 'alert',
29458             cn : [
29459                 {
29460                     tag : 'i',
29461                     cls : 'roo-alert-icon'
29462                     
29463                 },
29464                 {
29465                     tag : 'b',
29466                     cls : 'roo-alert-title',
29467                     html : this.title
29468                 },
29469                 {
29470                     tag : 'span',
29471                     cls : 'roo-alert-text',
29472                     html : this.html
29473                 }
29474             ]
29475         };
29476         
29477         if(this.faicon){
29478             cfg.cn[0].cls += ' fa ' + this.faicon;
29479         }
29480         
29481         if(this.weight){
29482             cfg.cls += ' alert-' + this.weight;
29483         }
29484         
29485         return cfg;
29486     },
29487     
29488     initEvents: function() 
29489     {
29490         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29491     },
29492     
29493     setTitle : function(str)
29494     {
29495         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29496     },
29497     
29498     setText : function(str)
29499     {
29500         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29501     },
29502     
29503     setWeight : function(weight)
29504     {
29505         if(this.weight){
29506             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29507         }
29508         
29509         this.weight = weight;
29510         
29511         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29512     },
29513     
29514     setIcon : function(icon)
29515     {
29516         if(this.faicon){
29517             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29518         }
29519         
29520         this.faicon = icon;
29521         
29522         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29523     },
29524     
29525     hide: function() 
29526     {
29527         this.el.hide();   
29528     },
29529     
29530     show: function() 
29531     {  
29532         this.el.show();   
29533     }
29534     
29535 });
29536
29537  
29538 /*
29539 * Licence: LGPL
29540 */
29541
29542 /**
29543  * @class Roo.bootstrap.UploadCropbox
29544  * @extends Roo.bootstrap.Component
29545  * Bootstrap UploadCropbox class
29546  * @cfg {String} emptyText show when image has been loaded
29547  * @cfg {String} rotateNotify show when image too small to rotate
29548  * @cfg {Number} errorTimeout default 3000
29549  * @cfg {Number} minWidth default 300
29550  * @cfg {Number} minHeight default 300
29551  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29552  * @cfg {Boolean} isDocument (true|false) default false
29553  * @cfg {String} url action url
29554  * @cfg {String} paramName default 'imageUpload'
29555  * @cfg {String} method default POST
29556  * @cfg {Boolean} loadMask (true|false) default true
29557  * @cfg {Boolean} loadingText default 'Loading...'
29558  * 
29559  * @constructor
29560  * Create a new UploadCropbox
29561  * @param {Object} config The config object
29562  */
29563
29564 Roo.bootstrap.UploadCropbox = function(config){
29565     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29566     
29567     this.addEvents({
29568         /**
29569          * @event beforeselectfile
29570          * Fire before select file
29571          * @param {Roo.bootstrap.UploadCropbox} this
29572          */
29573         "beforeselectfile" : true,
29574         /**
29575          * @event initial
29576          * Fire after initEvent
29577          * @param {Roo.bootstrap.UploadCropbox} this
29578          */
29579         "initial" : true,
29580         /**
29581          * @event crop
29582          * Fire after initEvent
29583          * @param {Roo.bootstrap.UploadCropbox} this
29584          * @param {String} data
29585          */
29586         "crop" : true,
29587         /**
29588          * @event prepare
29589          * Fire when preparing the file data
29590          * @param {Roo.bootstrap.UploadCropbox} this
29591          * @param {Object} file
29592          */
29593         "prepare" : true,
29594         /**
29595          * @event exception
29596          * Fire when get exception
29597          * @param {Roo.bootstrap.UploadCropbox} this
29598          * @param {XMLHttpRequest} xhr
29599          */
29600         "exception" : true,
29601         /**
29602          * @event beforeloadcanvas
29603          * Fire before load the canvas
29604          * @param {Roo.bootstrap.UploadCropbox} this
29605          * @param {String} src
29606          */
29607         "beforeloadcanvas" : true,
29608         /**
29609          * @event trash
29610          * Fire when trash image
29611          * @param {Roo.bootstrap.UploadCropbox} this
29612          */
29613         "trash" : true,
29614         /**
29615          * @event download
29616          * Fire when download the image
29617          * @param {Roo.bootstrap.UploadCropbox} this
29618          */
29619         "download" : true,
29620         /**
29621          * @event footerbuttonclick
29622          * Fire when footerbuttonclick
29623          * @param {Roo.bootstrap.UploadCropbox} this
29624          * @param {String} type
29625          */
29626         "footerbuttonclick" : true,
29627         /**
29628          * @event resize
29629          * Fire when resize
29630          * @param {Roo.bootstrap.UploadCropbox} this
29631          */
29632         "resize" : true,
29633         /**
29634          * @event rotate
29635          * Fire when rotate the image
29636          * @param {Roo.bootstrap.UploadCropbox} this
29637          * @param {String} pos
29638          */
29639         "rotate" : true,
29640         /**
29641          * @event inspect
29642          * Fire when inspect the file
29643          * @param {Roo.bootstrap.UploadCropbox} this
29644          * @param {Object} file
29645          */
29646         "inspect" : true,
29647         /**
29648          * @event upload
29649          * Fire when xhr upload the file
29650          * @param {Roo.bootstrap.UploadCropbox} this
29651          * @param {Object} data
29652          */
29653         "upload" : true,
29654         /**
29655          * @event arrange
29656          * Fire when arrange the file data
29657          * @param {Roo.bootstrap.UploadCropbox} this
29658          * @param {Object} formData
29659          */
29660         "arrange" : true
29661     });
29662     
29663     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29664 };
29665
29666 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29667     
29668     emptyText : 'Click to upload image',
29669     rotateNotify : 'Image is too small to rotate',
29670     errorTimeout : 3000,
29671     scale : 0,
29672     baseScale : 1,
29673     rotate : 0,
29674     dragable : false,
29675     pinching : false,
29676     mouseX : 0,
29677     mouseY : 0,
29678     cropData : false,
29679     minWidth : 300,
29680     minHeight : 300,
29681     file : false,
29682     exif : {},
29683     baseRotate : 1,
29684     cropType : 'image/jpeg',
29685     buttons : false,
29686     canvasLoaded : false,
29687     isDocument : false,
29688     method : 'POST',
29689     paramName : 'imageUpload',
29690     loadMask : true,
29691     loadingText : 'Loading...',
29692     maskEl : false,
29693     
29694     getAutoCreate : function()
29695     {
29696         var cfg = {
29697             tag : 'div',
29698             cls : 'roo-upload-cropbox',
29699             cn : [
29700                 {
29701                     tag : 'input',
29702                     cls : 'roo-upload-cropbox-selector',
29703                     type : 'file'
29704                 },
29705                 {
29706                     tag : 'div',
29707                     cls : 'roo-upload-cropbox-body',
29708                     style : 'cursor:pointer',
29709                     cn : [
29710                         {
29711                             tag : 'div',
29712                             cls : 'roo-upload-cropbox-preview'
29713                         },
29714                         {
29715                             tag : 'div',
29716                             cls : 'roo-upload-cropbox-thumb'
29717                         },
29718                         {
29719                             tag : 'div',
29720                             cls : 'roo-upload-cropbox-empty-notify',
29721                             html : this.emptyText
29722                         },
29723                         {
29724                             tag : 'div',
29725                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29726                             html : this.rotateNotify
29727                         }
29728                     ]
29729                 },
29730                 {
29731                     tag : 'div',
29732                     cls : 'roo-upload-cropbox-footer',
29733                     cn : {
29734                         tag : 'div',
29735                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29736                         cn : []
29737                     }
29738                 }
29739             ]
29740         };
29741         
29742         return cfg;
29743     },
29744     
29745     onRender : function(ct, position)
29746     {
29747         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29748         
29749         if (this.buttons.length) {
29750             
29751             Roo.each(this.buttons, function(bb) {
29752                 
29753                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29754                 
29755                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29756                 
29757             }, this);
29758         }
29759         
29760         if(this.loadMask){
29761             this.maskEl = this.el;
29762         }
29763     },
29764     
29765     initEvents : function()
29766     {
29767         this.urlAPI = (window.createObjectURL && window) || 
29768                                 (window.URL && URL.revokeObjectURL && URL) || 
29769                                 (window.webkitURL && webkitURL);
29770                         
29771         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29772         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29773         
29774         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29775         this.selectorEl.hide();
29776         
29777         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29778         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29779         
29780         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29781         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29782         this.thumbEl.hide();
29783         
29784         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29785         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29786         
29787         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29788         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29789         this.errorEl.hide();
29790         
29791         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29792         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29793         this.footerEl.hide();
29794         
29795         this.setThumbBoxSize();
29796         
29797         this.bind();
29798         
29799         this.resize();
29800         
29801         this.fireEvent('initial', this);
29802     },
29803
29804     bind : function()
29805     {
29806         var _this = this;
29807         
29808         window.addEventListener("resize", function() { _this.resize(); } );
29809         
29810         this.bodyEl.on('click', this.beforeSelectFile, this);
29811         
29812         if(Roo.isTouch){
29813             this.bodyEl.on('touchstart', this.onTouchStart, this);
29814             this.bodyEl.on('touchmove', this.onTouchMove, this);
29815             this.bodyEl.on('touchend', this.onTouchEnd, this);
29816         }
29817         
29818         if(!Roo.isTouch){
29819             this.bodyEl.on('mousedown', this.onMouseDown, this);
29820             this.bodyEl.on('mousemove', this.onMouseMove, this);
29821             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29822             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29823             Roo.get(document).on('mouseup', this.onMouseUp, this);
29824         }
29825         
29826         this.selectorEl.on('change', this.onFileSelected, this);
29827     },
29828     
29829     reset : function()
29830     {    
29831         this.scale = 0;
29832         this.baseScale = 1;
29833         this.rotate = 0;
29834         this.baseRotate = 1;
29835         this.dragable = false;
29836         this.pinching = false;
29837         this.mouseX = 0;
29838         this.mouseY = 0;
29839         this.cropData = false;
29840         this.notifyEl.dom.innerHTML = this.emptyText;
29841         
29842         this.selectorEl.dom.value = '';
29843         
29844     },
29845     
29846     resize : function()
29847     {
29848         if(this.fireEvent('resize', this) != false){
29849             this.setThumbBoxPosition();
29850             this.setCanvasPosition();
29851         }
29852     },
29853     
29854     onFooterButtonClick : function(e, el, o, type)
29855     {
29856         switch (type) {
29857             case 'rotate-left' :
29858                 this.onRotateLeft(e);
29859                 break;
29860             case 'rotate-right' :
29861                 this.onRotateRight(e);
29862                 break;
29863             case 'picture' :
29864                 this.beforeSelectFile(e);
29865                 break;
29866             case 'trash' :
29867                 this.trash(e);
29868                 break;
29869             case 'crop' :
29870                 this.crop(e);
29871                 break;
29872             case 'download' :
29873                 this.download(e);
29874                 break;
29875             default :
29876                 break;
29877         }
29878         
29879         this.fireEvent('footerbuttonclick', this, type);
29880     },
29881     
29882     beforeSelectFile : function(e)
29883     {
29884         e.preventDefault();
29885         
29886         if(this.fireEvent('beforeselectfile', this) != false){
29887             this.selectorEl.dom.click();
29888         }
29889     },
29890     
29891     onFileSelected : function(e)
29892     {
29893         e.preventDefault();
29894         
29895         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29896             return;
29897         }
29898         
29899         var file = this.selectorEl.dom.files[0];
29900         
29901         if(this.fireEvent('inspect', this, file) != false){
29902             this.prepare(file);
29903         }
29904         
29905     },
29906     
29907     trash : function(e)
29908     {
29909         this.fireEvent('trash', this);
29910     },
29911     
29912     download : function(e)
29913     {
29914         this.fireEvent('download', this);
29915     },
29916     
29917     loadCanvas : function(src)
29918     {   
29919         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29920             
29921             this.reset();
29922             
29923             this.imageEl = document.createElement('img');
29924             
29925             var _this = this;
29926             
29927             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29928             
29929             this.imageEl.src = src;
29930         }
29931     },
29932     
29933     onLoadCanvas : function()
29934     {   
29935         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29936         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29937         
29938         this.bodyEl.un('click', this.beforeSelectFile, this);
29939         
29940         this.notifyEl.hide();
29941         this.thumbEl.show();
29942         this.footerEl.show();
29943         
29944         this.baseRotateLevel();
29945         
29946         if(this.isDocument){
29947             this.setThumbBoxSize();
29948         }
29949         
29950         this.setThumbBoxPosition();
29951         
29952         this.baseScaleLevel();
29953         
29954         this.draw();
29955         
29956         this.resize();
29957         
29958         this.canvasLoaded = true;
29959         
29960         if(this.loadMask){
29961             this.maskEl.unmask();
29962         }
29963         
29964     },
29965     
29966     setCanvasPosition : function()
29967     {   
29968         if(!this.canvasEl){
29969             return;
29970         }
29971         
29972         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29973         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29974         
29975         this.previewEl.setLeft(pw);
29976         this.previewEl.setTop(ph);
29977         
29978     },
29979     
29980     onMouseDown : function(e)
29981     {   
29982         e.stopEvent();
29983         
29984         this.dragable = true;
29985         this.pinching = false;
29986         
29987         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29988             this.dragable = false;
29989             return;
29990         }
29991         
29992         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29993         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29994         
29995     },
29996     
29997     onMouseMove : function(e)
29998     {   
29999         e.stopEvent();
30000         
30001         if(!this.canvasLoaded){
30002             return;
30003         }
30004         
30005         if (!this.dragable){
30006             return;
30007         }
30008         
30009         var minX = Math.ceil(this.thumbEl.getLeft(true));
30010         var minY = Math.ceil(this.thumbEl.getTop(true));
30011         
30012         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30013         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30014         
30015         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30016         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30017         
30018         x = x - this.mouseX;
30019         y = y - this.mouseY;
30020         
30021         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30022         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30023         
30024         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30025         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30026         
30027         this.previewEl.setLeft(bgX);
30028         this.previewEl.setTop(bgY);
30029         
30030         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30031         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30032     },
30033     
30034     onMouseUp : function(e)
30035     {   
30036         e.stopEvent();
30037         
30038         this.dragable = false;
30039     },
30040     
30041     onMouseWheel : function(e)
30042     {   
30043         e.stopEvent();
30044         
30045         this.startScale = this.scale;
30046         
30047         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30048         
30049         if(!this.zoomable()){
30050             this.scale = this.startScale;
30051             return;
30052         }
30053         
30054         this.draw();
30055         
30056         return;
30057     },
30058     
30059     zoomable : function()
30060     {
30061         var minScale = this.thumbEl.getWidth() / this.minWidth;
30062         
30063         if(this.minWidth < this.minHeight){
30064             minScale = this.thumbEl.getHeight() / this.minHeight;
30065         }
30066         
30067         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30068         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30069         
30070         if(
30071                 this.isDocument &&
30072                 (this.rotate == 0 || this.rotate == 180) && 
30073                 (
30074                     width > this.imageEl.OriginWidth || 
30075                     height > this.imageEl.OriginHeight ||
30076                     (width < this.minWidth && height < this.minHeight)
30077                 )
30078         ){
30079             return false;
30080         }
30081         
30082         if(
30083                 this.isDocument &&
30084                 (this.rotate == 90 || this.rotate == 270) && 
30085                 (
30086                     width > this.imageEl.OriginWidth || 
30087                     height > this.imageEl.OriginHeight ||
30088                     (width < this.minHeight && height < this.minWidth)
30089                 )
30090         ){
30091             return false;
30092         }
30093         
30094         if(
30095                 !this.isDocument &&
30096                 (this.rotate == 0 || this.rotate == 180) && 
30097                 (
30098                     width < this.minWidth || 
30099                     width > this.imageEl.OriginWidth || 
30100                     height < this.minHeight || 
30101                     height > this.imageEl.OriginHeight
30102                 )
30103         ){
30104             return false;
30105         }
30106         
30107         if(
30108                 !this.isDocument &&
30109                 (this.rotate == 90 || this.rotate == 270) && 
30110                 (
30111                     width < this.minHeight || 
30112                     width > this.imageEl.OriginWidth || 
30113                     height < this.minWidth || 
30114                     height > this.imageEl.OriginHeight
30115                 )
30116         ){
30117             return false;
30118         }
30119         
30120         return true;
30121         
30122     },
30123     
30124     onRotateLeft : function(e)
30125     {   
30126         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30127             
30128             var minScale = this.thumbEl.getWidth() / this.minWidth;
30129             
30130             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30131             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30132             
30133             this.startScale = this.scale;
30134             
30135             while (this.getScaleLevel() < minScale){
30136             
30137                 this.scale = this.scale + 1;
30138                 
30139                 if(!this.zoomable()){
30140                     break;
30141                 }
30142                 
30143                 if(
30144                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30145                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30146                 ){
30147                     continue;
30148                 }
30149                 
30150                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30151
30152                 this.draw();
30153                 
30154                 return;
30155             }
30156             
30157             this.scale = this.startScale;
30158             
30159             this.onRotateFail();
30160             
30161             return false;
30162         }
30163         
30164         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30165
30166         if(this.isDocument){
30167             this.setThumbBoxSize();
30168             this.setThumbBoxPosition();
30169             this.setCanvasPosition();
30170         }
30171         
30172         this.draw();
30173         
30174         this.fireEvent('rotate', this, 'left');
30175         
30176     },
30177     
30178     onRotateRight : function(e)
30179     {
30180         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30181             
30182             var minScale = this.thumbEl.getWidth() / this.minWidth;
30183         
30184             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30185             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30186             
30187             this.startScale = this.scale;
30188             
30189             while (this.getScaleLevel() < minScale){
30190             
30191                 this.scale = this.scale + 1;
30192                 
30193                 if(!this.zoomable()){
30194                     break;
30195                 }
30196                 
30197                 if(
30198                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30199                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30200                 ){
30201                     continue;
30202                 }
30203                 
30204                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30205
30206                 this.draw();
30207                 
30208                 return;
30209             }
30210             
30211             this.scale = this.startScale;
30212             
30213             this.onRotateFail();
30214             
30215             return false;
30216         }
30217         
30218         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30219
30220         if(this.isDocument){
30221             this.setThumbBoxSize();
30222             this.setThumbBoxPosition();
30223             this.setCanvasPosition();
30224         }
30225         
30226         this.draw();
30227         
30228         this.fireEvent('rotate', this, 'right');
30229     },
30230     
30231     onRotateFail : function()
30232     {
30233         this.errorEl.show(true);
30234         
30235         var _this = this;
30236         
30237         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30238     },
30239     
30240     draw : function()
30241     {
30242         this.previewEl.dom.innerHTML = '';
30243         
30244         var canvasEl = document.createElement("canvas");
30245         
30246         var contextEl = canvasEl.getContext("2d");
30247         
30248         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30249         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30250         var center = this.imageEl.OriginWidth / 2;
30251         
30252         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30253             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30254             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30255             center = this.imageEl.OriginHeight / 2;
30256         }
30257         
30258         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30259         
30260         contextEl.translate(center, center);
30261         contextEl.rotate(this.rotate * Math.PI / 180);
30262
30263         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30264         
30265         this.canvasEl = document.createElement("canvas");
30266         
30267         this.contextEl = this.canvasEl.getContext("2d");
30268         
30269         switch (this.rotate) {
30270             case 0 :
30271                 
30272                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30273                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30274                 
30275                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30276                 
30277                 break;
30278             case 90 : 
30279                 
30280                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30281                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30282                 
30283                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30284                     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);
30285                     break;
30286                 }
30287                 
30288                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30289                 
30290                 break;
30291             case 180 :
30292                 
30293                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30294                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30295                 
30296                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30297                     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);
30298                     break;
30299                 }
30300                 
30301                 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);
30302                 
30303                 break;
30304             case 270 :
30305                 
30306                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30307                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30308         
30309                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30310                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30311                     break;
30312                 }
30313                 
30314                 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);
30315                 
30316                 break;
30317             default : 
30318                 break;
30319         }
30320         
30321         this.previewEl.appendChild(this.canvasEl);
30322         
30323         this.setCanvasPosition();
30324     },
30325     
30326     crop : function()
30327     {
30328         if(!this.canvasLoaded){
30329             return;
30330         }
30331         
30332         var imageCanvas = document.createElement("canvas");
30333         
30334         var imageContext = imageCanvas.getContext("2d");
30335         
30336         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30337         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30338         
30339         var center = imageCanvas.width / 2;
30340         
30341         imageContext.translate(center, center);
30342         
30343         imageContext.rotate(this.rotate * Math.PI / 180);
30344         
30345         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30346         
30347         var canvas = document.createElement("canvas");
30348         
30349         var context = canvas.getContext("2d");
30350                 
30351         canvas.width = this.minWidth;
30352         canvas.height = this.minHeight;
30353
30354         switch (this.rotate) {
30355             case 0 :
30356                 
30357                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30358                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30359                 
30360                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30361                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30362                 
30363                 var targetWidth = this.minWidth - 2 * x;
30364                 var targetHeight = this.minHeight - 2 * y;
30365                 
30366                 var scale = 1;
30367                 
30368                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30369                     scale = targetWidth / width;
30370                 }
30371                 
30372                 if(x > 0 && y == 0){
30373                     scale = targetHeight / height;
30374                 }
30375                 
30376                 if(x > 0 && y > 0){
30377                     scale = targetWidth / width;
30378                     
30379                     if(width < height){
30380                         scale = targetHeight / height;
30381                     }
30382                 }
30383                 
30384                 context.scale(scale, scale);
30385                 
30386                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30387                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30388
30389                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30390                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30391
30392                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30393                 
30394                 break;
30395             case 90 : 
30396                 
30397                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30398                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30399                 
30400                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30401                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30402                 
30403                 var targetWidth = this.minWidth - 2 * x;
30404                 var targetHeight = this.minHeight - 2 * y;
30405                 
30406                 var scale = 1;
30407                 
30408                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30409                     scale = targetWidth / width;
30410                 }
30411                 
30412                 if(x > 0 && y == 0){
30413                     scale = targetHeight / height;
30414                 }
30415                 
30416                 if(x > 0 && y > 0){
30417                     scale = targetWidth / width;
30418                     
30419                     if(width < height){
30420                         scale = targetHeight / height;
30421                     }
30422                 }
30423                 
30424                 context.scale(scale, scale);
30425                 
30426                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30427                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30428
30429                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30430                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30431                 
30432                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30433                 
30434                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30435                 
30436                 break;
30437             case 180 :
30438                 
30439                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30440                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30441                 
30442                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30443                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30444                 
30445                 var targetWidth = this.minWidth - 2 * x;
30446                 var targetHeight = this.minHeight - 2 * y;
30447                 
30448                 var scale = 1;
30449                 
30450                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30451                     scale = targetWidth / width;
30452                 }
30453                 
30454                 if(x > 0 && y == 0){
30455                     scale = targetHeight / height;
30456                 }
30457                 
30458                 if(x > 0 && y > 0){
30459                     scale = targetWidth / width;
30460                     
30461                     if(width < height){
30462                         scale = targetHeight / height;
30463                     }
30464                 }
30465                 
30466                 context.scale(scale, scale);
30467                 
30468                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30469                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30470
30471                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30472                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30473
30474                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30475                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30476                 
30477                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30478                 
30479                 break;
30480             case 270 :
30481                 
30482                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30483                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30484                 
30485                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30486                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30487                 
30488                 var targetWidth = this.minWidth - 2 * x;
30489                 var targetHeight = this.minHeight - 2 * y;
30490                 
30491                 var scale = 1;
30492                 
30493                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30494                     scale = targetWidth / width;
30495                 }
30496                 
30497                 if(x > 0 && y == 0){
30498                     scale = targetHeight / height;
30499                 }
30500                 
30501                 if(x > 0 && y > 0){
30502                     scale = targetWidth / width;
30503                     
30504                     if(width < height){
30505                         scale = targetHeight / height;
30506                     }
30507                 }
30508                 
30509                 context.scale(scale, scale);
30510                 
30511                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30512                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30513
30514                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30515                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30516                 
30517                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30518                 
30519                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30520                 
30521                 break;
30522             default : 
30523                 break;
30524         }
30525         
30526         this.cropData = canvas.toDataURL(this.cropType);
30527         
30528         if(this.fireEvent('crop', this, this.cropData) !== false){
30529             this.process(this.file, this.cropData);
30530         }
30531         
30532         return;
30533         
30534     },
30535     
30536     setThumbBoxSize : function()
30537     {
30538         var width, height;
30539         
30540         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30541             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30542             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30543             
30544             this.minWidth = width;
30545             this.minHeight = height;
30546             
30547             if(this.rotate == 90 || this.rotate == 270){
30548                 this.minWidth = height;
30549                 this.minHeight = width;
30550             }
30551         }
30552         
30553         height = 300;
30554         width = Math.ceil(this.minWidth * height / this.minHeight);
30555         
30556         if(this.minWidth > this.minHeight){
30557             width = 300;
30558             height = Math.ceil(this.minHeight * width / this.minWidth);
30559         }
30560         
30561         this.thumbEl.setStyle({
30562             width : width + 'px',
30563             height : height + 'px'
30564         });
30565
30566         return;
30567             
30568     },
30569     
30570     setThumbBoxPosition : function()
30571     {
30572         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30573         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30574         
30575         this.thumbEl.setLeft(x);
30576         this.thumbEl.setTop(y);
30577         
30578     },
30579     
30580     baseRotateLevel : function()
30581     {
30582         this.baseRotate = 1;
30583         
30584         if(
30585                 typeof(this.exif) != 'undefined' &&
30586                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30587                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30588         ){
30589             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30590         }
30591         
30592         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30593         
30594     },
30595     
30596     baseScaleLevel : function()
30597     {
30598         var width, height;
30599         
30600         if(this.isDocument){
30601             
30602             if(this.baseRotate == 6 || this.baseRotate == 8){
30603             
30604                 height = this.thumbEl.getHeight();
30605                 this.baseScale = height / this.imageEl.OriginWidth;
30606
30607                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30608                     width = this.thumbEl.getWidth();
30609                     this.baseScale = width / this.imageEl.OriginHeight;
30610                 }
30611
30612                 return;
30613             }
30614
30615             height = this.thumbEl.getHeight();
30616             this.baseScale = height / this.imageEl.OriginHeight;
30617
30618             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30619                 width = this.thumbEl.getWidth();
30620                 this.baseScale = width / this.imageEl.OriginWidth;
30621             }
30622
30623             return;
30624         }
30625         
30626         if(this.baseRotate == 6 || this.baseRotate == 8){
30627             
30628             width = this.thumbEl.getHeight();
30629             this.baseScale = width / this.imageEl.OriginHeight;
30630             
30631             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30632                 height = this.thumbEl.getWidth();
30633                 this.baseScale = height / this.imageEl.OriginHeight;
30634             }
30635             
30636             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30637                 height = this.thumbEl.getWidth();
30638                 this.baseScale = height / this.imageEl.OriginHeight;
30639                 
30640                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30641                     width = this.thumbEl.getHeight();
30642                     this.baseScale = width / this.imageEl.OriginWidth;
30643                 }
30644             }
30645             
30646             return;
30647         }
30648         
30649         width = this.thumbEl.getWidth();
30650         this.baseScale = width / this.imageEl.OriginWidth;
30651         
30652         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30653             height = this.thumbEl.getHeight();
30654             this.baseScale = height / this.imageEl.OriginHeight;
30655         }
30656         
30657         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30658             
30659             height = this.thumbEl.getHeight();
30660             this.baseScale = height / this.imageEl.OriginHeight;
30661             
30662             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30663                 width = this.thumbEl.getWidth();
30664                 this.baseScale = width / this.imageEl.OriginWidth;
30665             }
30666             
30667         }
30668         
30669         return;
30670     },
30671     
30672     getScaleLevel : function()
30673     {
30674         return this.baseScale * Math.pow(1.1, this.scale);
30675     },
30676     
30677     onTouchStart : function(e)
30678     {
30679         if(!this.canvasLoaded){
30680             this.beforeSelectFile(e);
30681             return;
30682         }
30683         
30684         var touches = e.browserEvent.touches;
30685         
30686         if(!touches){
30687             return;
30688         }
30689         
30690         if(touches.length == 1){
30691             this.onMouseDown(e);
30692             return;
30693         }
30694         
30695         if(touches.length != 2){
30696             return;
30697         }
30698         
30699         var coords = [];
30700         
30701         for(var i = 0, finger; finger = touches[i]; i++){
30702             coords.push(finger.pageX, finger.pageY);
30703         }
30704         
30705         var x = Math.pow(coords[0] - coords[2], 2);
30706         var y = Math.pow(coords[1] - coords[3], 2);
30707         
30708         this.startDistance = Math.sqrt(x + y);
30709         
30710         this.startScale = this.scale;
30711         
30712         this.pinching = true;
30713         this.dragable = false;
30714         
30715     },
30716     
30717     onTouchMove : function(e)
30718     {
30719         if(!this.pinching && !this.dragable){
30720             return;
30721         }
30722         
30723         var touches = e.browserEvent.touches;
30724         
30725         if(!touches){
30726             return;
30727         }
30728         
30729         if(this.dragable){
30730             this.onMouseMove(e);
30731             return;
30732         }
30733         
30734         var coords = [];
30735         
30736         for(var i = 0, finger; finger = touches[i]; i++){
30737             coords.push(finger.pageX, finger.pageY);
30738         }
30739         
30740         var x = Math.pow(coords[0] - coords[2], 2);
30741         var y = Math.pow(coords[1] - coords[3], 2);
30742         
30743         this.endDistance = Math.sqrt(x + y);
30744         
30745         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30746         
30747         if(!this.zoomable()){
30748             this.scale = this.startScale;
30749             return;
30750         }
30751         
30752         this.draw();
30753         
30754     },
30755     
30756     onTouchEnd : function(e)
30757     {
30758         this.pinching = false;
30759         this.dragable = false;
30760         
30761     },
30762     
30763     process : function(file, crop)
30764     {
30765         if(this.loadMask){
30766             this.maskEl.mask(this.loadingText);
30767         }
30768         
30769         this.xhr = new XMLHttpRequest();
30770         
30771         file.xhr = this.xhr;
30772
30773         this.xhr.open(this.method, this.url, true);
30774         
30775         var headers = {
30776             "Accept": "application/json",
30777             "Cache-Control": "no-cache",
30778             "X-Requested-With": "XMLHttpRequest"
30779         };
30780         
30781         for (var headerName in headers) {
30782             var headerValue = headers[headerName];
30783             if (headerValue) {
30784                 this.xhr.setRequestHeader(headerName, headerValue);
30785             }
30786         }
30787         
30788         var _this = this;
30789         
30790         this.xhr.onload = function()
30791         {
30792             _this.xhrOnLoad(_this.xhr);
30793         }
30794         
30795         this.xhr.onerror = function()
30796         {
30797             _this.xhrOnError(_this.xhr);
30798         }
30799         
30800         var formData = new FormData();
30801
30802         formData.append('returnHTML', 'NO');
30803         
30804         if(crop){
30805             formData.append('crop', crop);
30806         }
30807         
30808         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30809             formData.append(this.paramName, file, file.name);
30810         }
30811         
30812         if(typeof(file.filename) != 'undefined'){
30813             formData.append('filename', file.filename);
30814         }
30815         
30816         if(typeof(file.mimetype) != 'undefined'){
30817             formData.append('mimetype', file.mimetype);
30818         }
30819         
30820         if(this.fireEvent('arrange', this, formData) != false){
30821             this.xhr.send(formData);
30822         };
30823     },
30824     
30825     xhrOnLoad : function(xhr)
30826     {
30827         if(this.loadMask){
30828             this.maskEl.unmask();
30829         }
30830         
30831         if (xhr.readyState !== 4) {
30832             this.fireEvent('exception', this, xhr);
30833             return;
30834         }
30835
30836         var response = Roo.decode(xhr.responseText);
30837         
30838         if(!response.success){
30839             this.fireEvent('exception', this, xhr);
30840             return;
30841         }
30842         
30843         var response = Roo.decode(xhr.responseText);
30844         
30845         this.fireEvent('upload', this, response);
30846         
30847     },
30848     
30849     xhrOnError : function()
30850     {
30851         if(this.loadMask){
30852             this.maskEl.unmask();
30853         }
30854         
30855         Roo.log('xhr on error');
30856         
30857         var response = Roo.decode(xhr.responseText);
30858           
30859         Roo.log(response);
30860         
30861     },
30862     
30863     prepare : function(file)
30864     {   
30865         if(this.loadMask){
30866             this.maskEl.mask(this.loadingText);
30867         }
30868         
30869         this.file = false;
30870         this.exif = {};
30871         
30872         if(typeof(file) === 'string'){
30873             this.loadCanvas(file);
30874             return;
30875         }
30876         
30877         if(!file || !this.urlAPI){
30878             return;
30879         }
30880         
30881         this.file = file;
30882         this.cropType = file.type;
30883         
30884         var _this = this;
30885         
30886         if(this.fireEvent('prepare', this, this.file) != false){
30887             
30888             var reader = new FileReader();
30889             
30890             reader.onload = function (e) {
30891                 if (e.target.error) {
30892                     Roo.log(e.target.error);
30893                     return;
30894                 }
30895                 
30896                 var buffer = e.target.result,
30897                     dataView = new DataView(buffer),
30898                     offset = 2,
30899                     maxOffset = dataView.byteLength - 4,
30900                     markerBytes,
30901                     markerLength;
30902                 
30903                 if (dataView.getUint16(0) === 0xffd8) {
30904                     while (offset < maxOffset) {
30905                         markerBytes = dataView.getUint16(offset);
30906                         
30907                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30908                             markerLength = dataView.getUint16(offset + 2) + 2;
30909                             if (offset + markerLength > dataView.byteLength) {
30910                                 Roo.log('Invalid meta data: Invalid segment size.');
30911                                 break;
30912                             }
30913                             
30914                             if(markerBytes == 0xffe1){
30915                                 _this.parseExifData(
30916                                     dataView,
30917                                     offset,
30918                                     markerLength
30919                                 );
30920                             }
30921                             
30922                             offset += markerLength;
30923                             
30924                             continue;
30925                         }
30926                         
30927                         break;
30928                     }
30929                     
30930                 }
30931                 
30932                 var url = _this.urlAPI.createObjectURL(_this.file);
30933                 
30934                 _this.loadCanvas(url);
30935                 
30936                 return;
30937             }
30938             
30939             reader.readAsArrayBuffer(this.file);
30940             
30941         }
30942         
30943     },
30944     
30945     parseExifData : function(dataView, offset, length)
30946     {
30947         var tiffOffset = offset + 10,
30948             littleEndian,
30949             dirOffset;
30950     
30951         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30952             // No Exif data, might be XMP data instead
30953             return;
30954         }
30955         
30956         // Check for the ASCII code for "Exif" (0x45786966):
30957         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30958             // No Exif data, might be XMP data instead
30959             return;
30960         }
30961         if (tiffOffset + 8 > dataView.byteLength) {
30962             Roo.log('Invalid Exif data: Invalid segment size.');
30963             return;
30964         }
30965         // Check for the two null bytes:
30966         if (dataView.getUint16(offset + 8) !== 0x0000) {
30967             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30968             return;
30969         }
30970         // Check the byte alignment:
30971         switch (dataView.getUint16(tiffOffset)) {
30972         case 0x4949:
30973             littleEndian = true;
30974             break;
30975         case 0x4D4D:
30976             littleEndian = false;
30977             break;
30978         default:
30979             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30980             return;
30981         }
30982         // Check for the TIFF tag marker (0x002A):
30983         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30984             Roo.log('Invalid Exif data: Missing TIFF marker.');
30985             return;
30986         }
30987         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30988         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30989         
30990         this.parseExifTags(
30991             dataView,
30992             tiffOffset,
30993             tiffOffset + dirOffset,
30994             littleEndian
30995         );
30996     },
30997     
30998     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30999     {
31000         var tagsNumber,
31001             dirEndOffset,
31002             i;
31003         if (dirOffset + 6 > dataView.byteLength) {
31004             Roo.log('Invalid Exif data: Invalid directory offset.');
31005             return;
31006         }
31007         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31008         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31009         if (dirEndOffset + 4 > dataView.byteLength) {
31010             Roo.log('Invalid Exif data: Invalid directory size.');
31011             return;
31012         }
31013         for (i = 0; i < tagsNumber; i += 1) {
31014             this.parseExifTag(
31015                 dataView,
31016                 tiffOffset,
31017                 dirOffset + 2 + 12 * i, // tag offset
31018                 littleEndian
31019             );
31020         }
31021         // Return the offset to the next directory:
31022         return dataView.getUint32(dirEndOffset, littleEndian);
31023     },
31024     
31025     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31026     {
31027         var tag = dataView.getUint16(offset, littleEndian);
31028         
31029         this.exif[tag] = this.getExifValue(
31030             dataView,
31031             tiffOffset,
31032             offset,
31033             dataView.getUint16(offset + 2, littleEndian), // tag type
31034             dataView.getUint32(offset + 4, littleEndian), // tag length
31035             littleEndian
31036         );
31037     },
31038     
31039     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31040     {
31041         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31042             tagSize,
31043             dataOffset,
31044             values,
31045             i,
31046             str,
31047             c;
31048     
31049         if (!tagType) {
31050             Roo.log('Invalid Exif data: Invalid tag type.');
31051             return;
31052         }
31053         
31054         tagSize = tagType.size * length;
31055         // Determine if the value is contained in the dataOffset bytes,
31056         // or if the value at the dataOffset is a pointer to the actual data:
31057         dataOffset = tagSize > 4 ?
31058                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31059         if (dataOffset + tagSize > dataView.byteLength) {
31060             Roo.log('Invalid Exif data: Invalid data offset.');
31061             return;
31062         }
31063         if (length === 1) {
31064             return tagType.getValue(dataView, dataOffset, littleEndian);
31065         }
31066         values = [];
31067         for (i = 0; i < length; i += 1) {
31068             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31069         }
31070         
31071         if (tagType.ascii) {
31072             str = '';
31073             // Concatenate the chars:
31074             for (i = 0; i < values.length; i += 1) {
31075                 c = values[i];
31076                 // Ignore the terminating NULL byte(s):
31077                 if (c === '\u0000') {
31078                     break;
31079                 }
31080                 str += c;
31081             }
31082             return str;
31083         }
31084         return values;
31085     }
31086     
31087 });
31088
31089 Roo.apply(Roo.bootstrap.UploadCropbox, {
31090     tags : {
31091         'Orientation': 0x0112
31092     },
31093     
31094     Orientation: {
31095             1: 0, //'top-left',
31096 //            2: 'top-right',
31097             3: 180, //'bottom-right',
31098 //            4: 'bottom-left',
31099 //            5: 'left-top',
31100             6: 90, //'right-top',
31101 //            7: 'right-bottom',
31102             8: 270 //'left-bottom'
31103     },
31104     
31105     exifTagTypes : {
31106         // byte, 8-bit unsigned int:
31107         1: {
31108             getValue: function (dataView, dataOffset) {
31109                 return dataView.getUint8(dataOffset);
31110             },
31111             size: 1
31112         },
31113         // ascii, 8-bit byte:
31114         2: {
31115             getValue: function (dataView, dataOffset) {
31116                 return String.fromCharCode(dataView.getUint8(dataOffset));
31117             },
31118             size: 1,
31119             ascii: true
31120         },
31121         // short, 16 bit int:
31122         3: {
31123             getValue: function (dataView, dataOffset, littleEndian) {
31124                 return dataView.getUint16(dataOffset, littleEndian);
31125             },
31126             size: 2
31127         },
31128         // long, 32 bit int:
31129         4: {
31130             getValue: function (dataView, dataOffset, littleEndian) {
31131                 return dataView.getUint32(dataOffset, littleEndian);
31132             },
31133             size: 4
31134         },
31135         // rational = two long values, first is numerator, second is denominator:
31136         5: {
31137             getValue: function (dataView, dataOffset, littleEndian) {
31138                 return dataView.getUint32(dataOffset, littleEndian) /
31139                     dataView.getUint32(dataOffset + 4, littleEndian);
31140             },
31141             size: 8
31142         },
31143         // slong, 32 bit signed int:
31144         9: {
31145             getValue: function (dataView, dataOffset, littleEndian) {
31146                 return dataView.getInt32(dataOffset, littleEndian);
31147             },
31148             size: 4
31149         },
31150         // srational, two slongs, first is numerator, second is denominator:
31151         10: {
31152             getValue: function (dataView, dataOffset, littleEndian) {
31153                 return dataView.getInt32(dataOffset, littleEndian) /
31154                     dataView.getInt32(dataOffset + 4, littleEndian);
31155             },
31156             size: 8
31157         }
31158     },
31159     
31160     footer : {
31161         STANDARD : [
31162             {
31163                 tag : 'div',
31164                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31165                 action : 'rotate-left',
31166                 cn : [
31167                     {
31168                         tag : 'button',
31169                         cls : 'btn btn-default',
31170                         html : '<i class="fa fa-undo"></i>'
31171                     }
31172                 ]
31173             },
31174             {
31175                 tag : 'div',
31176                 cls : 'btn-group roo-upload-cropbox-picture',
31177                 action : 'picture',
31178                 cn : [
31179                     {
31180                         tag : 'button',
31181                         cls : 'btn btn-default',
31182                         html : '<i class="fa fa-picture-o"></i>'
31183                     }
31184                 ]
31185             },
31186             {
31187                 tag : 'div',
31188                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31189                 action : 'rotate-right',
31190                 cn : [
31191                     {
31192                         tag : 'button',
31193                         cls : 'btn btn-default',
31194                         html : '<i class="fa fa-repeat"></i>'
31195                     }
31196                 ]
31197             }
31198         ],
31199         DOCUMENT : [
31200             {
31201                 tag : 'div',
31202                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31203                 action : 'rotate-left',
31204                 cn : [
31205                     {
31206                         tag : 'button',
31207                         cls : 'btn btn-default',
31208                         html : '<i class="fa fa-undo"></i>'
31209                     }
31210                 ]
31211             },
31212             {
31213                 tag : 'div',
31214                 cls : 'btn-group roo-upload-cropbox-download',
31215                 action : 'download',
31216                 cn : [
31217                     {
31218                         tag : 'button',
31219                         cls : 'btn btn-default',
31220                         html : '<i class="fa fa-download"></i>'
31221                     }
31222                 ]
31223             },
31224             {
31225                 tag : 'div',
31226                 cls : 'btn-group roo-upload-cropbox-crop',
31227                 action : 'crop',
31228                 cn : [
31229                     {
31230                         tag : 'button',
31231                         cls : 'btn btn-default',
31232                         html : '<i class="fa fa-crop"></i>'
31233                     }
31234                 ]
31235             },
31236             {
31237                 tag : 'div',
31238                 cls : 'btn-group roo-upload-cropbox-trash',
31239                 action : 'trash',
31240                 cn : [
31241                     {
31242                         tag : 'button',
31243                         cls : 'btn btn-default',
31244                         html : '<i class="fa fa-trash"></i>'
31245                     }
31246                 ]
31247             },
31248             {
31249                 tag : 'div',
31250                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31251                 action : 'rotate-right',
31252                 cn : [
31253                     {
31254                         tag : 'button',
31255                         cls : 'btn btn-default',
31256                         html : '<i class="fa fa-repeat"></i>'
31257                     }
31258                 ]
31259             }
31260         ],
31261         ROTATOR : [
31262             {
31263                 tag : 'div',
31264                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31265                 action : 'rotate-left',
31266                 cn : [
31267                     {
31268                         tag : 'button',
31269                         cls : 'btn btn-default',
31270                         html : '<i class="fa fa-undo"></i>'
31271                     }
31272                 ]
31273             },
31274             {
31275                 tag : 'div',
31276                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31277                 action : 'rotate-right',
31278                 cn : [
31279                     {
31280                         tag : 'button',
31281                         cls : 'btn btn-default',
31282                         html : '<i class="fa fa-repeat"></i>'
31283                     }
31284                 ]
31285             }
31286         ]
31287     }
31288 });
31289
31290 /*
31291 * Licence: LGPL
31292 */
31293
31294 /**
31295  * @class Roo.bootstrap.DocumentManager
31296  * @extends Roo.bootstrap.Component
31297  * Bootstrap DocumentManager class
31298  * @cfg {String} paramName default 'imageUpload'
31299  * @cfg {String} toolTipName default 'filename'
31300  * @cfg {String} method default POST
31301  * @cfg {String} url action url
31302  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31303  * @cfg {Boolean} multiple multiple upload default true
31304  * @cfg {Number} thumbSize default 300
31305  * @cfg {String} fieldLabel
31306  * @cfg {Number} labelWidth default 4
31307  * @cfg {String} labelAlign (left|top) default left
31308  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31309 * @cfg {Number} labellg set the width of label (1-12)
31310  * @cfg {Number} labelmd set the width of label (1-12)
31311  * @cfg {Number} labelsm set the width of label (1-12)
31312  * @cfg {Number} labelxs set the width of label (1-12)
31313  * 
31314  * @constructor
31315  * Create a new DocumentManager
31316  * @param {Object} config The config object
31317  */
31318
31319 Roo.bootstrap.DocumentManager = function(config){
31320     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31321     
31322     this.files = [];
31323     this.delegates = [];
31324     
31325     this.addEvents({
31326         /**
31327          * @event initial
31328          * Fire when initial the DocumentManager
31329          * @param {Roo.bootstrap.DocumentManager} this
31330          */
31331         "initial" : true,
31332         /**
31333          * @event inspect
31334          * inspect selected file
31335          * @param {Roo.bootstrap.DocumentManager} this
31336          * @param {File} file
31337          */
31338         "inspect" : true,
31339         /**
31340          * @event exception
31341          * Fire when xhr load exception
31342          * @param {Roo.bootstrap.DocumentManager} this
31343          * @param {XMLHttpRequest} xhr
31344          */
31345         "exception" : true,
31346         /**
31347          * @event afterupload
31348          * Fire when xhr load exception
31349          * @param {Roo.bootstrap.DocumentManager} this
31350          * @param {XMLHttpRequest} xhr
31351          */
31352         "afterupload" : true,
31353         /**
31354          * @event prepare
31355          * prepare the form data
31356          * @param {Roo.bootstrap.DocumentManager} this
31357          * @param {Object} formData
31358          */
31359         "prepare" : true,
31360         /**
31361          * @event remove
31362          * Fire when remove the file
31363          * @param {Roo.bootstrap.DocumentManager} this
31364          * @param {Object} file
31365          */
31366         "remove" : true,
31367         /**
31368          * @event refresh
31369          * Fire after refresh the file
31370          * @param {Roo.bootstrap.DocumentManager} this
31371          */
31372         "refresh" : true,
31373         /**
31374          * @event click
31375          * Fire after click the image
31376          * @param {Roo.bootstrap.DocumentManager} this
31377          * @param {Object} file
31378          */
31379         "click" : true,
31380         /**
31381          * @event edit
31382          * Fire when upload a image and editable set to true
31383          * @param {Roo.bootstrap.DocumentManager} this
31384          * @param {Object} file
31385          */
31386         "edit" : true,
31387         /**
31388          * @event beforeselectfile
31389          * Fire before select file
31390          * @param {Roo.bootstrap.DocumentManager} this
31391          */
31392         "beforeselectfile" : true,
31393         /**
31394          * @event process
31395          * Fire before process file
31396          * @param {Roo.bootstrap.DocumentManager} this
31397          * @param {Object} file
31398          */
31399         "process" : true,
31400         /**
31401          * @event previewrendered
31402          * Fire when preview rendered
31403          * @param {Roo.bootstrap.DocumentManager} this
31404          * @param {Object} file
31405          */
31406         "previewrendered" : true,
31407         /**
31408          */
31409         "previewResize" : true
31410         
31411     });
31412 };
31413
31414 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31415     
31416     boxes : 0,
31417     inputName : '',
31418     thumbSize : 300,
31419     multiple : true,
31420     files : false,
31421     method : 'POST',
31422     url : '',
31423     paramName : 'imageUpload',
31424     toolTipName : 'filename',
31425     fieldLabel : '',
31426     labelWidth : 4,
31427     labelAlign : 'left',
31428     editable : true,
31429     delegates : false,
31430     xhr : false, 
31431     
31432     labellg : 0,
31433     labelmd : 0,
31434     labelsm : 0,
31435     labelxs : 0,
31436     
31437     getAutoCreate : function()
31438     {   
31439         var managerWidget = {
31440             tag : 'div',
31441             cls : 'roo-document-manager',
31442             cn : [
31443                 {
31444                     tag : 'input',
31445                     cls : 'roo-document-manager-selector',
31446                     type : 'file'
31447                 },
31448                 {
31449                     tag : 'div',
31450                     cls : 'roo-document-manager-uploader',
31451                     cn : [
31452                         {
31453                             tag : 'div',
31454                             cls : 'roo-document-manager-upload-btn',
31455                             html : '<i class="fa fa-plus"></i>'
31456                         }
31457                     ]
31458                     
31459                 }
31460             ]
31461         };
31462         
31463         var content = [
31464             {
31465                 tag : 'div',
31466                 cls : 'column col-md-12',
31467                 cn : managerWidget
31468             }
31469         ];
31470         
31471         if(this.fieldLabel.length){
31472             
31473             content = [
31474                 {
31475                     tag : 'div',
31476                     cls : 'column col-md-12',
31477                     html : this.fieldLabel
31478                 },
31479                 {
31480                     tag : 'div',
31481                     cls : 'column col-md-12',
31482                     cn : managerWidget
31483                 }
31484             ];
31485
31486             if(this.labelAlign == 'left'){
31487                 content = [
31488                     {
31489                         tag : 'div',
31490                         cls : 'column',
31491                         html : this.fieldLabel
31492                     },
31493                     {
31494                         tag : 'div',
31495                         cls : 'column',
31496                         cn : managerWidget
31497                     }
31498                 ];
31499                 
31500                 if(this.labelWidth > 12){
31501                     content[0].style = "width: " + this.labelWidth + 'px';
31502                 }
31503
31504                 if(this.labelWidth < 13 && this.labelmd == 0){
31505                     this.labelmd = this.labelWidth;
31506                 }
31507
31508                 if(this.labellg > 0){
31509                     content[0].cls += ' col-lg-' + this.labellg;
31510                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31511                 }
31512
31513                 if(this.labelmd > 0){
31514                     content[0].cls += ' col-md-' + this.labelmd;
31515                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31516                 }
31517
31518                 if(this.labelsm > 0){
31519                     content[0].cls += ' col-sm-' + this.labelsm;
31520                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31521                 }
31522
31523                 if(this.labelxs > 0){
31524                     content[0].cls += ' col-xs-' + this.labelxs;
31525                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31526                 }
31527                 
31528             }
31529         }
31530         
31531         var cfg = {
31532             tag : 'div',
31533             cls : 'row clearfix',
31534             cn : content
31535         };
31536         
31537         return cfg;
31538         
31539     },
31540     
31541     initEvents : function()
31542     {
31543         this.managerEl = this.el.select('.roo-document-manager', true).first();
31544         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31545         
31546         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31547         this.selectorEl.hide();
31548         
31549         if(this.multiple){
31550             this.selectorEl.attr('multiple', 'multiple');
31551         }
31552         
31553         this.selectorEl.on('change', this.onFileSelected, this);
31554         
31555         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31556         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31557         
31558         this.uploader.on('click', this.onUploaderClick, this);
31559         
31560         this.renderProgressDialog();
31561         
31562         var _this = this;
31563         
31564         window.addEventListener("resize", function() { _this.refresh(); } );
31565         
31566         this.fireEvent('initial', this);
31567     },
31568     
31569     renderProgressDialog : function()
31570     {
31571         var _this = this;
31572         
31573         this.progressDialog = new Roo.bootstrap.Modal({
31574             cls : 'roo-document-manager-progress-dialog',
31575             allow_close : false,
31576             animate : false,
31577             title : '',
31578             buttons : [
31579                 {
31580                     name  :'cancel',
31581                     weight : 'danger',
31582                     html : 'Cancel'
31583                 }
31584             ], 
31585             listeners : { 
31586                 btnclick : function() {
31587                     _this.uploadCancel();
31588                     this.hide();
31589                 }
31590             }
31591         });
31592          
31593         this.progressDialog.render(Roo.get(document.body));
31594          
31595         this.progress = new Roo.bootstrap.Progress({
31596             cls : 'roo-document-manager-progress',
31597             active : true,
31598             striped : true
31599         });
31600         
31601         this.progress.render(this.progressDialog.getChildContainer());
31602         
31603         this.progressBar = new Roo.bootstrap.ProgressBar({
31604             cls : 'roo-document-manager-progress-bar',
31605             aria_valuenow : 0,
31606             aria_valuemin : 0,
31607             aria_valuemax : 12,
31608             panel : 'success'
31609         });
31610         
31611         this.progressBar.render(this.progress.getChildContainer());
31612     },
31613     
31614     onUploaderClick : function(e)
31615     {
31616         e.preventDefault();
31617      
31618         if(this.fireEvent('beforeselectfile', this) != false){
31619             this.selectorEl.dom.click();
31620         }
31621         
31622     },
31623     
31624     onFileSelected : function(e)
31625     {
31626         e.preventDefault();
31627         
31628         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31629             return;
31630         }
31631         
31632         Roo.each(this.selectorEl.dom.files, function(file){
31633             if(this.fireEvent('inspect', this, file) != false){
31634                 this.files.push(file);
31635             }
31636         }, this);
31637         
31638         this.queue();
31639         
31640     },
31641     
31642     queue : function()
31643     {
31644         this.selectorEl.dom.value = '';
31645         
31646         if(!this.files || !this.files.length){
31647             return;
31648         }
31649         
31650         if(this.boxes > 0 && this.files.length > this.boxes){
31651             this.files = this.files.slice(0, this.boxes);
31652         }
31653         
31654         this.uploader.show();
31655         
31656         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31657             this.uploader.hide();
31658         }
31659         
31660         var _this = this;
31661         
31662         var files = [];
31663         
31664         var docs = [];
31665         
31666         Roo.each(this.files, function(file){
31667             
31668             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31669                 var f = this.renderPreview(file);
31670                 files.push(f);
31671                 return;
31672             }
31673             
31674             if(file.type.indexOf('image') != -1){
31675                 this.delegates.push(
31676                     (function(){
31677                         _this.process(file);
31678                     }).createDelegate(this)
31679                 );
31680         
31681                 return;
31682             }
31683             
31684             docs.push(
31685                 (function(){
31686                     _this.process(file);
31687                 }).createDelegate(this)
31688             );
31689             
31690         }, this);
31691         
31692         this.files = files;
31693         
31694         this.delegates = this.delegates.concat(docs);
31695         
31696         if(!this.delegates.length){
31697             this.refresh();
31698             return;
31699         }
31700         
31701         this.progressBar.aria_valuemax = this.delegates.length;
31702         
31703         this.arrange();
31704         
31705         return;
31706     },
31707     
31708     arrange : function()
31709     {
31710         if(!this.delegates.length){
31711             this.progressDialog.hide();
31712             this.refresh();
31713             return;
31714         }
31715         
31716         var delegate = this.delegates.shift();
31717         
31718         this.progressDialog.show();
31719         
31720         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31721         
31722         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31723         
31724         delegate();
31725     },
31726     
31727     refresh : function()
31728     {
31729         this.uploader.show();
31730         
31731         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31732             this.uploader.hide();
31733         }
31734         
31735         Roo.isTouch ? this.closable(false) : this.closable(true);
31736         
31737         this.fireEvent('refresh', this);
31738     },
31739     
31740     onRemove : function(e, el, o)
31741     {
31742         e.preventDefault();
31743         
31744         this.fireEvent('remove', this, o);
31745         
31746     },
31747     
31748     remove : function(o)
31749     {
31750         var files = [];
31751         
31752         Roo.each(this.files, function(file){
31753             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31754                 files.push(file);
31755                 return;
31756             }
31757
31758             o.target.remove();
31759
31760         }, this);
31761         
31762         this.files = files;
31763         
31764         this.refresh();
31765     },
31766     
31767     clear : function()
31768     {
31769         Roo.each(this.files, function(file){
31770             if(!file.target){
31771                 return;
31772             }
31773             
31774             file.target.remove();
31775
31776         }, this);
31777         
31778         this.files = [];
31779         
31780         this.refresh();
31781     },
31782     
31783     onClick : function(e, el, o)
31784     {
31785         e.preventDefault();
31786         
31787         this.fireEvent('click', this, o);
31788         
31789     },
31790     
31791     closable : function(closable)
31792     {
31793         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31794             
31795             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31796             
31797             if(closable){
31798                 el.show();
31799                 return;
31800             }
31801             
31802             el.hide();
31803             
31804         }, this);
31805     },
31806     
31807     xhrOnLoad : function(xhr)
31808     {
31809         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31810             el.remove();
31811         }, this);
31812         
31813         if (xhr.readyState !== 4) {
31814             this.arrange();
31815             this.fireEvent('exception', this, xhr);
31816             return;
31817         }
31818
31819         var response = Roo.decode(xhr.responseText);
31820         
31821         if(!response.success){
31822             this.arrange();
31823             this.fireEvent('exception', this, xhr);
31824             return;
31825         }
31826         
31827         var file = this.renderPreview(response.data);
31828         
31829         this.files.push(file);
31830         
31831         this.arrange();
31832         
31833         this.fireEvent('afterupload', this, xhr);
31834         
31835     },
31836     
31837     xhrOnError : function(xhr)
31838     {
31839         Roo.log('xhr on error');
31840         
31841         var response = Roo.decode(xhr.responseText);
31842           
31843         Roo.log(response);
31844         
31845         this.arrange();
31846     },
31847     
31848     process : function(file)
31849     {
31850         if(this.fireEvent('process', this, file) !== false){
31851             if(this.editable && file.type.indexOf('image') != -1){
31852                 this.fireEvent('edit', this, file);
31853                 return;
31854             }
31855
31856             this.uploadStart(file, false);
31857
31858             return;
31859         }
31860         
31861     },
31862     
31863     uploadStart : function(file, crop)
31864     {
31865         this.xhr = new XMLHttpRequest();
31866         
31867         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31868             this.arrange();
31869             return;
31870         }
31871         
31872         file.xhr = this.xhr;
31873             
31874         this.managerEl.createChild({
31875             tag : 'div',
31876             cls : 'roo-document-manager-loading',
31877             cn : [
31878                 {
31879                     tag : 'div',
31880                     tooltip : file.name,
31881                     cls : 'roo-document-manager-thumb',
31882                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31883                 }
31884             ]
31885
31886         });
31887
31888         this.xhr.open(this.method, this.url, true);
31889         
31890         var headers = {
31891             "Accept": "application/json",
31892             "Cache-Control": "no-cache",
31893             "X-Requested-With": "XMLHttpRequest"
31894         };
31895         
31896         for (var headerName in headers) {
31897             var headerValue = headers[headerName];
31898             if (headerValue) {
31899                 this.xhr.setRequestHeader(headerName, headerValue);
31900             }
31901         }
31902         
31903         var _this = this;
31904         
31905         this.xhr.onload = function()
31906         {
31907             _this.xhrOnLoad(_this.xhr);
31908         }
31909         
31910         this.xhr.onerror = function()
31911         {
31912             _this.xhrOnError(_this.xhr);
31913         }
31914         
31915         var formData = new FormData();
31916
31917         formData.append('returnHTML', 'NO');
31918         
31919         if(crop){
31920             formData.append('crop', crop);
31921         }
31922         
31923         formData.append(this.paramName, file, file.name);
31924         
31925         var options = {
31926             file : file, 
31927             manually : false
31928         };
31929         
31930         if(this.fireEvent('prepare', this, formData, options) != false){
31931             
31932             if(options.manually){
31933                 return;
31934             }
31935             
31936             this.xhr.send(formData);
31937             return;
31938         };
31939         
31940         this.uploadCancel();
31941     },
31942     
31943     uploadCancel : function()
31944     {
31945         if (this.xhr) {
31946             this.xhr.abort();
31947         }
31948         
31949         this.delegates = [];
31950         
31951         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31952             el.remove();
31953         }, this);
31954         
31955         this.arrange();
31956     },
31957     
31958     renderPreview : function(file)
31959     {
31960         if(typeof(file.target) != 'undefined' && file.target){
31961             return file;
31962         }
31963         
31964         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31965         
31966         var previewEl = this.managerEl.createChild({
31967             tag : 'div',
31968             cls : 'roo-document-manager-preview',
31969             cn : [
31970                 {
31971                     tag : 'div',
31972                     tooltip : file[this.toolTipName],
31973                     cls : 'roo-document-manager-thumb',
31974                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31975                 },
31976                 {
31977                     tag : 'button',
31978                     cls : 'close',
31979                     html : '<i class="fa fa-times-circle"></i>'
31980                 }
31981             ]
31982         });
31983
31984         var close = previewEl.select('button.close', true).first();
31985
31986         close.on('click', this.onRemove, this, file);
31987
31988         file.target = previewEl;
31989
31990         var image = previewEl.select('img', true).first();
31991         
31992         var _this = this;
31993         
31994         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31995         
31996         image.on('click', this.onClick, this, file);
31997         
31998         this.fireEvent('previewrendered', this, file);
31999         
32000         return file;
32001         
32002     },
32003     
32004     onPreviewLoad : function(file, image)
32005     {
32006         if(typeof(file.target) == 'undefined' || !file.target){
32007             return;
32008         }
32009         
32010         var width = image.dom.naturalWidth || image.dom.width;
32011         var height = image.dom.naturalHeight || image.dom.height;
32012         
32013         if(!this.previewResize) {
32014             return;
32015         }
32016         
32017         if(width > height){
32018             file.target.addClass('wide');
32019             return;
32020         }
32021         
32022         file.target.addClass('tall');
32023         return;
32024         
32025     },
32026     
32027     uploadFromSource : function(file, crop)
32028     {
32029         this.xhr = new XMLHttpRequest();
32030         
32031         this.managerEl.createChild({
32032             tag : 'div',
32033             cls : 'roo-document-manager-loading',
32034             cn : [
32035                 {
32036                     tag : 'div',
32037                     tooltip : file.name,
32038                     cls : 'roo-document-manager-thumb',
32039                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32040                 }
32041             ]
32042
32043         });
32044
32045         this.xhr.open(this.method, this.url, true);
32046         
32047         var headers = {
32048             "Accept": "application/json",
32049             "Cache-Control": "no-cache",
32050             "X-Requested-With": "XMLHttpRequest"
32051         };
32052         
32053         for (var headerName in headers) {
32054             var headerValue = headers[headerName];
32055             if (headerValue) {
32056                 this.xhr.setRequestHeader(headerName, headerValue);
32057             }
32058         }
32059         
32060         var _this = this;
32061         
32062         this.xhr.onload = function()
32063         {
32064             _this.xhrOnLoad(_this.xhr);
32065         }
32066         
32067         this.xhr.onerror = function()
32068         {
32069             _this.xhrOnError(_this.xhr);
32070         }
32071         
32072         var formData = new FormData();
32073
32074         formData.append('returnHTML', 'NO');
32075         
32076         formData.append('crop', crop);
32077         
32078         if(typeof(file.filename) != 'undefined'){
32079             formData.append('filename', file.filename);
32080         }
32081         
32082         if(typeof(file.mimetype) != 'undefined'){
32083             formData.append('mimetype', file.mimetype);
32084         }
32085         
32086         Roo.log(formData);
32087         
32088         if(this.fireEvent('prepare', this, formData) != false){
32089             this.xhr.send(formData);
32090         };
32091     }
32092 });
32093
32094 /*
32095 * Licence: LGPL
32096 */
32097
32098 /**
32099  * @class Roo.bootstrap.DocumentViewer
32100  * @extends Roo.bootstrap.Component
32101  * Bootstrap DocumentViewer class
32102  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32103  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32104  * 
32105  * @constructor
32106  * Create a new DocumentViewer
32107  * @param {Object} config The config object
32108  */
32109
32110 Roo.bootstrap.DocumentViewer = function(config){
32111     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32112     
32113     this.addEvents({
32114         /**
32115          * @event initial
32116          * Fire after initEvent
32117          * @param {Roo.bootstrap.DocumentViewer} this
32118          */
32119         "initial" : true,
32120         /**
32121          * @event click
32122          * Fire after click
32123          * @param {Roo.bootstrap.DocumentViewer} this
32124          */
32125         "click" : true,
32126         /**
32127          * @event download
32128          * Fire after download button
32129          * @param {Roo.bootstrap.DocumentViewer} this
32130          */
32131         "download" : true,
32132         /**
32133          * @event trash
32134          * Fire after trash button
32135          * @param {Roo.bootstrap.DocumentViewer} this
32136          */
32137         "trash" : true
32138         
32139     });
32140 };
32141
32142 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32143     
32144     showDownload : true,
32145     
32146     showTrash : true,
32147     
32148     getAutoCreate : function()
32149     {
32150         var cfg = {
32151             tag : 'div',
32152             cls : 'roo-document-viewer',
32153             cn : [
32154                 {
32155                     tag : 'div',
32156                     cls : 'roo-document-viewer-body',
32157                     cn : [
32158                         {
32159                             tag : 'div',
32160                             cls : 'roo-document-viewer-thumb',
32161                             cn : [
32162                                 {
32163                                     tag : 'img',
32164                                     cls : 'roo-document-viewer-image'
32165                                 }
32166                             ]
32167                         }
32168                     ]
32169                 },
32170                 {
32171                     tag : 'div',
32172                     cls : 'roo-document-viewer-footer',
32173                     cn : {
32174                         tag : 'div',
32175                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32176                         cn : [
32177                             {
32178                                 tag : 'div',
32179                                 cls : 'btn-group roo-document-viewer-download',
32180                                 cn : [
32181                                     {
32182                                         tag : 'button',
32183                                         cls : 'btn btn-default',
32184                                         html : '<i class="fa fa-download"></i>'
32185                                     }
32186                                 ]
32187                             },
32188                             {
32189                                 tag : 'div',
32190                                 cls : 'btn-group roo-document-viewer-trash',
32191                                 cn : [
32192                                     {
32193                                         tag : 'button',
32194                                         cls : 'btn btn-default',
32195                                         html : '<i class="fa fa-trash"></i>'
32196                                     }
32197                                 ]
32198                             }
32199                         ]
32200                     }
32201                 }
32202             ]
32203         };
32204         
32205         return cfg;
32206     },
32207     
32208     initEvents : function()
32209     {
32210         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32211         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32212         
32213         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32214         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32215         
32216         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32217         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32218         
32219         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32220         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32221         
32222         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32223         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32224         
32225         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32226         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32227         
32228         this.bodyEl.on('click', this.onClick, this);
32229         this.downloadBtn.on('click', this.onDownload, this);
32230         this.trashBtn.on('click', this.onTrash, this);
32231         
32232         this.downloadBtn.hide();
32233         this.trashBtn.hide();
32234         
32235         if(this.showDownload){
32236             this.downloadBtn.show();
32237         }
32238         
32239         if(this.showTrash){
32240             this.trashBtn.show();
32241         }
32242         
32243         if(!this.showDownload && !this.showTrash) {
32244             this.footerEl.hide();
32245         }
32246         
32247     },
32248     
32249     initial : function()
32250     {
32251         this.fireEvent('initial', this);
32252         
32253     },
32254     
32255     onClick : function(e)
32256     {
32257         e.preventDefault();
32258         
32259         this.fireEvent('click', this);
32260     },
32261     
32262     onDownload : function(e)
32263     {
32264         e.preventDefault();
32265         
32266         this.fireEvent('download', this);
32267     },
32268     
32269     onTrash : function(e)
32270     {
32271         e.preventDefault();
32272         
32273         this.fireEvent('trash', this);
32274     }
32275     
32276 });
32277 /*
32278  * - LGPL
32279  *
32280  * nav progress bar
32281  * 
32282  */
32283
32284 /**
32285  * @class Roo.bootstrap.NavProgressBar
32286  * @extends Roo.bootstrap.Component
32287  * Bootstrap NavProgressBar class
32288  * 
32289  * @constructor
32290  * Create a new nav progress bar
32291  * @param {Object} config The config object
32292  */
32293
32294 Roo.bootstrap.NavProgressBar = function(config){
32295     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32296
32297     this.bullets = this.bullets || [];
32298    
32299 //    Roo.bootstrap.NavProgressBar.register(this);
32300      this.addEvents({
32301         /**
32302              * @event changed
32303              * Fires when the active item changes
32304              * @param {Roo.bootstrap.NavProgressBar} this
32305              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32306              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32307          */
32308         'changed': true
32309      });
32310     
32311 };
32312
32313 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32314     
32315     bullets : [],
32316     barItems : [],
32317     
32318     getAutoCreate : function()
32319     {
32320         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32321         
32322         cfg = {
32323             tag : 'div',
32324             cls : 'roo-navigation-bar-group',
32325             cn : [
32326                 {
32327                     tag : 'div',
32328                     cls : 'roo-navigation-top-bar'
32329                 },
32330                 {
32331                     tag : 'div',
32332                     cls : 'roo-navigation-bullets-bar',
32333                     cn : [
32334                         {
32335                             tag : 'ul',
32336                             cls : 'roo-navigation-bar'
32337                         }
32338                     ]
32339                 },
32340                 
32341                 {
32342                     tag : 'div',
32343                     cls : 'roo-navigation-bottom-bar'
32344                 }
32345             ]
32346             
32347         };
32348         
32349         return cfg;
32350         
32351     },
32352     
32353     initEvents: function() 
32354     {
32355         
32356     },
32357     
32358     onRender : function(ct, position) 
32359     {
32360         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32361         
32362         if(this.bullets.length){
32363             Roo.each(this.bullets, function(b){
32364                this.addItem(b);
32365             }, this);
32366         }
32367         
32368         this.format();
32369         
32370     },
32371     
32372     addItem : function(cfg)
32373     {
32374         var item = new Roo.bootstrap.NavProgressItem(cfg);
32375         
32376         item.parentId = this.id;
32377         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32378         
32379         if(cfg.html){
32380             var top = new Roo.bootstrap.Element({
32381                 tag : 'div',
32382                 cls : 'roo-navigation-bar-text'
32383             });
32384             
32385             var bottom = new Roo.bootstrap.Element({
32386                 tag : 'div',
32387                 cls : 'roo-navigation-bar-text'
32388             });
32389             
32390             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32391             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32392             
32393             var topText = new Roo.bootstrap.Element({
32394                 tag : 'span',
32395                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32396             });
32397             
32398             var bottomText = new Roo.bootstrap.Element({
32399                 tag : 'span',
32400                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32401             });
32402             
32403             topText.onRender(top.el, null);
32404             bottomText.onRender(bottom.el, null);
32405             
32406             item.topEl = top;
32407             item.bottomEl = bottom;
32408         }
32409         
32410         this.barItems.push(item);
32411         
32412         return item;
32413     },
32414     
32415     getActive : function()
32416     {
32417         var active = false;
32418         
32419         Roo.each(this.barItems, function(v){
32420             
32421             if (!v.isActive()) {
32422                 return;
32423             }
32424             
32425             active = v;
32426             return false;
32427             
32428         });
32429         
32430         return active;
32431     },
32432     
32433     setActiveItem : function(item)
32434     {
32435         var prev = false;
32436         
32437         Roo.each(this.barItems, function(v){
32438             if (v.rid == item.rid) {
32439                 return ;
32440             }
32441             
32442             if (v.isActive()) {
32443                 v.setActive(false);
32444                 prev = v;
32445             }
32446         });
32447
32448         item.setActive(true);
32449         
32450         this.fireEvent('changed', this, item, prev);
32451     },
32452     
32453     getBarItem: function(rid)
32454     {
32455         var ret = false;
32456         
32457         Roo.each(this.barItems, function(e) {
32458             if (e.rid != rid) {
32459                 return;
32460             }
32461             
32462             ret =  e;
32463             return false;
32464         });
32465         
32466         return ret;
32467     },
32468     
32469     indexOfItem : function(item)
32470     {
32471         var index = false;
32472         
32473         Roo.each(this.barItems, function(v, i){
32474             
32475             if (v.rid != item.rid) {
32476                 return;
32477             }
32478             
32479             index = i;
32480             return false
32481         });
32482         
32483         return index;
32484     },
32485     
32486     setActiveNext : function()
32487     {
32488         var i = this.indexOfItem(this.getActive());
32489         
32490         if (i > this.barItems.length) {
32491             return;
32492         }
32493         
32494         this.setActiveItem(this.barItems[i+1]);
32495     },
32496     
32497     setActivePrev : function()
32498     {
32499         var i = this.indexOfItem(this.getActive());
32500         
32501         if (i  < 1) {
32502             return;
32503         }
32504         
32505         this.setActiveItem(this.barItems[i-1]);
32506     },
32507     
32508     format : function()
32509     {
32510         if(!this.barItems.length){
32511             return;
32512         }
32513      
32514         var width = 100 / this.barItems.length;
32515         
32516         Roo.each(this.barItems, function(i){
32517             i.el.setStyle('width', width + '%');
32518             i.topEl.el.setStyle('width', width + '%');
32519             i.bottomEl.el.setStyle('width', width + '%');
32520         }, this);
32521         
32522     }
32523     
32524 });
32525 /*
32526  * - LGPL
32527  *
32528  * Nav Progress Item
32529  * 
32530  */
32531
32532 /**
32533  * @class Roo.bootstrap.NavProgressItem
32534  * @extends Roo.bootstrap.Component
32535  * Bootstrap NavProgressItem class
32536  * @cfg {String} rid the reference id
32537  * @cfg {Boolean} active (true|false) Is item active default false
32538  * @cfg {Boolean} disabled (true|false) Is item active default false
32539  * @cfg {String} html
32540  * @cfg {String} position (top|bottom) text position default bottom
32541  * @cfg {String} icon show icon instead of number
32542  * 
32543  * @constructor
32544  * Create a new NavProgressItem
32545  * @param {Object} config The config object
32546  */
32547 Roo.bootstrap.NavProgressItem = function(config){
32548     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32549     this.addEvents({
32550         // raw events
32551         /**
32552          * @event click
32553          * The raw click event for the entire grid.
32554          * @param {Roo.bootstrap.NavProgressItem} this
32555          * @param {Roo.EventObject} e
32556          */
32557         "click" : true
32558     });
32559    
32560 };
32561
32562 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32563     
32564     rid : '',
32565     active : false,
32566     disabled : false,
32567     html : '',
32568     position : 'bottom',
32569     icon : false,
32570     
32571     getAutoCreate : function()
32572     {
32573         var iconCls = 'roo-navigation-bar-item-icon';
32574         
32575         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32576         
32577         var cfg = {
32578             tag: 'li',
32579             cls: 'roo-navigation-bar-item',
32580             cn : [
32581                 {
32582                     tag : 'i',
32583                     cls : iconCls
32584                 }
32585             ]
32586         };
32587         
32588         if(this.active){
32589             cfg.cls += ' active';
32590         }
32591         if(this.disabled){
32592             cfg.cls += ' disabled';
32593         }
32594         
32595         return cfg;
32596     },
32597     
32598     disable : function()
32599     {
32600         this.setDisabled(true);
32601     },
32602     
32603     enable : function()
32604     {
32605         this.setDisabled(false);
32606     },
32607     
32608     initEvents: function() 
32609     {
32610         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32611         
32612         this.iconEl.on('click', this.onClick, this);
32613     },
32614     
32615     onClick : function(e)
32616     {
32617         e.preventDefault();
32618         
32619         if(this.disabled){
32620             return;
32621         }
32622         
32623         if(this.fireEvent('click', this, e) === false){
32624             return;
32625         };
32626         
32627         this.parent().setActiveItem(this);
32628     },
32629     
32630     isActive: function () 
32631     {
32632         return this.active;
32633     },
32634     
32635     setActive : function(state)
32636     {
32637         if(this.active == state){
32638             return;
32639         }
32640         
32641         this.active = state;
32642         
32643         if (state) {
32644             this.el.addClass('active');
32645             return;
32646         }
32647         
32648         this.el.removeClass('active');
32649         
32650         return;
32651     },
32652     
32653     setDisabled : function(state)
32654     {
32655         if(this.disabled == state){
32656             return;
32657         }
32658         
32659         this.disabled = state;
32660         
32661         if (state) {
32662             this.el.addClass('disabled');
32663             return;
32664         }
32665         
32666         this.el.removeClass('disabled');
32667     },
32668     
32669     tooltipEl : function()
32670     {
32671         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32672     }
32673 });
32674  
32675
32676  /*
32677  * - LGPL
32678  *
32679  * FieldLabel
32680  * 
32681  */
32682
32683 /**
32684  * @class Roo.bootstrap.FieldLabel
32685  * @extends Roo.bootstrap.Component
32686  * Bootstrap FieldLabel class
32687  * @cfg {String} html contents of the element
32688  * @cfg {String} tag tag of the element default label
32689  * @cfg {String} cls class of the element
32690  * @cfg {String} target label target 
32691  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32692  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32693  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32694  * @cfg {String} iconTooltip default "This field is required"
32695  * @cfg {String} indicatorpos (left|right) default left
32696  * 
32697  * @constructor
32698  * Create a new FieldLabel
32699  * @param {Object} config The config object
32700  */
32701
32702 Roo.bootstrap.FieldLabel = function(config){
32703     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32704     
32705     this.addEvents({
32706             /**
32707              * @event invalid
32708              * Fires after the field has been marked as invalid.
32709              * @param {Roo.form.FieldLabel} this
32710              * @param {String} msg The validation message
32711              */
32712             invalid : true,
32713             /**
32714              * @event valid
32715              * Fires after the field has been validated with no errors.
32716              * @param {Roo.form.FieldLabel} this
32717              */
32718             valid : true
32719         });
32720 };
32721
32722 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32723     
32724     tag: 'label',
32725     cls: '',
32726     html: '',
32727     target: '',
32728     allowBlank : true,
32729     invalidClass : 'has-warning',
32730     validClass : 'has-success',
32731     iconTooltip : 'This field is required',
32732     indicatorpos : 'left',
32733     
32734     getAutoCreate : function(){
32735         
32736         var cls = "";
32737         if (!this.allowBlank) {
32738             cls  = "visible";
32739         }
32740         
32741         var cfg = {
32742             tag : this.tag,
32743             cls : 'roo-bootstrap-field-label ' + this.cls,
32744             for : this.target,
32745             cn : [
32746                 {
32747                     tag : 'i',
32748                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32749                     tooltip : this.iconTooltip
32750                 },
32751                 {
32752                     tag : 'span',
32753                     html : this.html
32754                 }
32755             ] 
32756         };
32757         
32758         if(this.indicatorpos == 'right'){
32759             var cfg = {
32760                 tag : this.tag,
32761                 cls : 'roo-bootstrap-field-label ' + this.cls,
32762                 for : this.target,
32763                 cn : [
32764                     {
32765                         tag : 'span',
32766                         html : this.html
32767                     },
32768                     {
32769                         tag : 'i',
32770                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32771                         tooltip : this.iconTooltip
32772                     }
32773                 ] 
32774             };
32775         }
32776         
32777         return cfg;
32778     },
32779     
32780     initEvents: function() 
32781     {
32782         Roo.bootstrap.Element.superclass.initEvents.call(this);
32783         
32784         this.indicator = this.indicatorEl();
32785         
32786         if(this.indicator){
32787             this.indicator.removeClass('visible');
32788             this.indicator.addClass('invisible');
32789         }
32790         
32791         Roo.bootstrap.FieldLabel.register(this);
32792     },
32793     
32794     indicatorEl : function()
32795     {
32796         var indicator = this.el.select('i.roo-required-indicator',true).first();
32797         
32798         if(!indicator){
32799             return false;
32800         }
32801         
32802         return indicator;
32803         
32804     },
32805     
32806     /**
32807      * Mark this field as valid
32808      */
32809     markValid : function()
32810     {
32811         if(this.indicator){
32812             this.indicator.removeClass('visible');
32813             this.indicator.addClass('invisible');
32814         }
32815         if (Roo.bootstrap.version == 3) {
32816             this.el.removeClass(this.invalidClass);
32817             this.el.addClass(this.validClass);
32818         } else {
32819             this.el.removeClass('is-invalid');
32820             this.el.addClass('is-valid');
32821         }
32822         
32823         
32824         this.fireEvent('valid', this);
32825     },
32826     
32827     /**
32828      * Mark this field as invalid
32829      * @param {String} msg The validation message
32830      */
32831     markInvalid : function(msg)
32832     {
32833         if(this.indicator){
32834             this.indicator.removeClass('invisible');
32835             this.indicator.addClass('visible');
32836         }
32837           if (Roo.bootstrap.version == 3) {
32838             this.el.removeClass(this.validClass);
32839             this.el.addClass(this.invalidClass);
32840         } else {
32841             this.el.removeClass('is-valid');
32842             this.el.addClass('is-invalid');
32843         }
32844         
32845         
32846         this.fireEvent('invalid', this, msg);
32847     }
32848     
32849    
32850 });
32851
32852 Roo.apply(Roo.bootstrap.FieldLabel, {
32853     
32854     groups: {},
32855     
32856      /**
32857     * register a FieldLabel Group
32858     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32859     */
32860     register : function(label)
32861     {
32862         if(this.groups.hasOwnProperty(label.target)){
32863             return;
32864         }
32865      
32866         this.groups[label.target] = label;
32867         
32868     },
32869     /**
32870     * fetch a FieldLabel Group based on the target
32871     * @param {string} target
32872     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32873     */
32874     get: function(target) {
32875         if (typeof(this.groups[target]) == 'undefined') {
32876             return false;
32877         }
32878         
32879         return this.groups[target] ;
32880     }
32881 });
32882
32883  
32884
32885  /*
32886  * - LGPL
32887  *
32888  * page DateSplitField.
32889  * 
32890  */
32891
32892
32893 /**
32894  * @class Roo.bootstrap.DateSplitField
32895  * @extends Roo.bootstrap.Component
32896  * Bootstrap DateSplitField class
32897  * @cfg {string} fieldLabel - the label associated
32898  * @cfg {Number} labelWidth set the width of label (0-12)
32899  * @cfg {String} labelAlign (top|left)
32900  * @cfg {Boolean} dayAllowBlank (true|false) default false
32901  * @cfg {Boolean} monthAllowBlank (true|false) default false
32902  * @cfg {Boolean} yearAllowBlank (true|false) default false
32903  * @cfg {string} dayPlaceholder 
32904  * @cfg {string} monthPlaceholder
32905  * @cfg {string} yearPlaceholder
32906  * @cfg {string} dayFormat default 'd'
32907  * @cfg {string} monthFormat default 'm'
32908  * @cfg {string} yearFormat default 'Y'
32909  * @cfg {Number} labellg set the width of label (1-12)
32910  * @cfg {Number} labelmd set the width of label (1-12)
32911  * @cfg {Number} labelsm set the width of label (1-12)
32912  * @cfg {Number} labelxs set the width of label (1-12)
32913
32914  *     
32915  * @constructor
32916  * Create a new DateSplitField
32917  * @param {Object} config The config object
32918  */
32919
32920 Roo.bootstrap.DateSplitField = function(config){
32921     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32922     
32923     this.addEvents({
32924         // raw events
32925          /**
32926          * @event years
32927          * getting the data of years
32928          * @param {Roo.bootstrap.DateSplitField} this
32929          * @param {Object} years
32930          */
32931         "years" : true,
32932         /**
32933          * @event days
32934          * getting the data of days
32935          * @param {Roo.bootstrap.DateSplitField} this
32936          * @param {Object} days
32937          */
32938         "days" : true,
32939         /**
32940          * @event invalid
32941          * Fires after the field has been marked as invalid.
32942          * @param {Roo.form.Field} this
32943          * @param {String} msg The validation message
32944          */
32945         invalid : true,
32946        /**
32947          * @event valid
32948          * Fires after the field has been validated with no errors.
32949          * @param {Roo.form.Field} this
32950          */
32951         valid : true
32952     });
32953 };
32954
32955 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32956     
32957     fieldLabel : '',
32958     labelAlign : 'top',
32959     labelWidth : 3,
32960     dayAllowBlank : false,
32961     monthAllowBlank : false,
32962     yearAllowBlank : false,
32963     dayPlaceholder : '',
32964     monthPlaceholder : '',
32965     yearPlaceholder : '',
32966     dayFormat : 'd',
32967     monthFormat : 'm',
32968     yearFormat : 'Y',
32969     isFormField : true,
32970     labellg : 0,
32971     labelmd : 0,
32972     labelsm : 0,
32973     labelxs : 0,
32974     
32975     getAutoCreate : function()
32976     {
32977         var cfg = {
32978             tag : 'div',
32979             cls : 'row roo-date-split-field-group',
32980             cn : [
32981                 {
32982                     tag : 'input',
32983                     type : 'hidden',
32984                     cls : 'form-hidden-field roo-date-split-field-group-value',
32985                     name : this.name
32986                 }
32987             ]
32988         };
32989         
32990         var labelCls = 'col-md-12';
32991         var contentCls = 'col-md-4';
32992         
32993         if(this.fieldLabel){
32994             
32995             var label = {
32996                 tag : 'div',
32997                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32998                 cn : [
32999                     {
33000                         tag : 'label',
33001                         html : this.fieldLabel
33002                     }
33003                 ]
33004             };
33005             
33006             if(this.labelAlign == 'left'){
33007             
33008                 if(this.labelWidth > 12){
33009                     label.style = "width: " + this.labelWidth + 'px';
33010                 }
33011
33012                 if(this.labelWidth < 13 && this.labelmd == 0){
33013                     this.labelmd = this.labelWidth;
33014                 }
33015
33016                 if(this.labellg > 0){
33017                     labelCls = ' col-lg-' + this.labellg;
33018                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33019                 }
33020
33021                 if(this.labelmd > 0){
33022                     labelCls = ' col-md-' + this.labelmd;
33023                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33024                 }
33025
33026                 if(this.labelsm > 0){
33027                     labelCls = ' col-sm-' + this.labelsm;
33028                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33029                 }
33030
33031                 if(this.labelxs > 0){
33032                     labelCls = ' col-xs-' + this.labelxs;
33033                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33034                 }
33035             }
33036             
33037             label.cls += ' ' + labelCls;
33038             
33039             cfg.cn.push(label);
33040         }
33041         
33042         Roo.each(['day', 'month', 'year'], function(t){
33043             cfg.cn.push({
33044                 tag : 'div',
33045                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33046             });
33047         }, this);
33048         
33049         return cfg;
33050     },
33051     
33052     inputEl: function ()
33053     {
33054         return this.el.select('.roo-date-split-field-group-value', true).first();
33055     },
33056     
33057     onRender : function(ct, position) 
33058     {
33059         var _this = this;
33060         
33061         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33062         
33063         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33064         
33065         this.dayField = new Roo.bootstrap.ComboBox({
33066             allowBlank : this.dayAllowBlank,
33067             alwaysQuery : true,
33068             displayField : 'value',
33069             editable : false,
33070             fieldLabel : '',
33071             forceSelection : true,
33072             mode : 'local',
33073             placeholder : this.dayPlaceholder,
33074             selectOnFocus : true,
33075             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33076             triggerAction : 'all',
33077             typeAhead : true,
33078             valueField : 'value',
33079             store : new Roo.data.SimpleStore({
33080                 data : (function() {    
33081                     var days = [];
33082                     _this.fireEvent('days', _this, days);
33083                     return days;
33084                 })(),
33085                 fields : [ 'value' ]
33086             }),
33087             listeners : {
33088                 select : function (_self, record, index)
33089                 {
33090                     _this.setValue(_this.getValue());
33091                 }
33092             }
33093         });
33094
33095         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33096         
33097         this.monthField = new Roo.bootstrap.MonthField({
33098             after : '<i class=\"fa fa-calendar\"></i>',
33099             allowBlank : this.monthAllowBlank,
33100             placeholder : this.monthPlaceholder,
33101             readOnly : true,
33102             listeners : {
33103                 render : function (_self)
33104                 {
33105                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33106                         e.preventDefault();
33107                         _self.focus();
33108                     });
33109                 },
33110                 select : function (_self, oldvalue, newvalue)
33111                 {
33112                     _this.setValue(_this.getValue());
33113                 }
33114             }
33115         });
33116         
33117         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33118         
33119         this.yearField = new Roo.bootstrap.ComboBox({
33120             allowBlank : this.yearAllowBlank,
33121             alwaysQuery : true,
33122             displayField : 'value',
33123             editable : false,
33124             fieldLabel : '',
33125             forceSelection : true,
33126             mode : 'local',
33127             placeholder : this.yearPlaceholder,
33128             selectOnFocus : true,
33129             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33130             triggerAction : 'all',
33131             typeAhead : true,
33132             valueField : 'value',
33133             store : new Roo.data.SimpleStore({
33134                 data : (function() {
33135                     var years = [];
33136                     _this.fireEvent('years', _this, years);
33137                     return years;
33138                 })(),
33139                 fields : [ 'value' ]
33140             }),
33141             listeners : {
33142                 select : function (_self, record, index)
33143                 {
33144                     _this.setValue(_this.getValue());
33145                 }
33146             }
33147         });
33148
33149         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33150     },
33151     
33152     setValue : function(v, format)
33153     {
33154         this.inputEl.dom.value = v;
33155         
33156         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33157         
33158         var d = Date.parseDate(v, f);
33159         
33160         if(!d){
33161             this.validate();
33162             return;
33163         }
33164         
33165         this.setDay(d.format(this.dayFormat));
33166         this.setMonth(d.format(this.monthFormat));
33167         this.setYear(d.format(this.yearFormat));
33168         
33169         this.validate();
33170         
33171         return;
33172     },
33173     
33174     setDay : function(v)
33175     {
33176         this.dayField.setValue(v);
33177         this.inputEl.dom.value = this.getValue();
33178         this.validate();
33179         return;
33180     },
33181     
33182     setMonth : function(v)
33183     {
33184         this.monthField.setValue(v, true);
33185         this.inputEl.dom.value = this.getValue();
33186         this.validate();
33187         return;
33188     },
33189     
33190     setYear : function(v)
33191     {
33192         this.yearField.setValue(v);
33193         this.inputEl.dom.value = this.getValue();
33194         this.validate();
33195         return;
33196     },
33197     
33198     getDay : function()
33199     {
33200         return this.dayField.getValue();
33201     },
33202     
33203     getMonth : function()
33204     {
33205         return this.monthField.getValue();
33206     },
33207     
33208     getYear : function()
33209     {
33210         return this.yearField.getValue();
33211     },
33212     
33213     getValue : function()
33214     {
33215         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33216         
33217         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33218         
33219         return date;
33220     },
33221     
33222     reset : function()
33223     {
33224         this.setDay('');
33225         this.setMonth('');
33226         this.setYear('');
33227         this.inputEl.dom.value = '';
33228         this.validate();
33229         return;
33230     },
33231     
33232     validate : function()
33233     {
33234         var d = this.dayField.validate();
33235         var m = this.monthField.validate();
33236         var y = this.yearField.validate();
33237         
33238         var valid = true;
33239         
33240         if(
33241                 (!this.dayAllowBlank && !d) ||
33242                 (!this.monthAllowBlank && !m) ||
33243                 (!this.yearAllowBlank && !y)
33244         ){
33245             valid = false;
33246         }
33247         
33248         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33249             return valid;
33250         }
33251         
33252         if(valid){
33253             this.markValid();
33254             return valid;
33255         }
33256         
33257         this.markInvalid();
33258         
33259         return valid;
33260     },
33261     
33262     markValid : function()
33263     {
33264         
33265         var label = this.el.select('label', true).first();
33266         var icon = this.el.select('i.fa-star', true).first();
33267
33268         if(label && icon){
33269             icon.remove();
33270         }
33271         
33272         this.fireEvent('valid', this);
33273     },
33274     
33275      /**
33276      * Mark this field as invalid
33277      * @param {String} msg The validation message
33278      */
33279     markInvalid : function(msg)
33280     {
33281         
33282         var label = this.el.select('label', true).first();
33283         var icon = this.el.select('i.fa-star', true).first();
33284
33285         if(label && !icon){
33286             this.el.select('.roo-date-split-field-label', true).createChild({
33287                 tag : 'i',
33288                 cls : 'text-danger fa fa-lg fa-star',
33289                 tooltip : 'This field is required',
33290                 style : 'margin-right:5px;'
33291             }, label, true);
33292         }
33293         
33294         this.fireEvent('invalid', this, msg);
33295     },
33296     
33297     clearInvalid : function()
33298     {
33299         var label = this.el.select('label', true).first();
33300         var icon = this.el.select('i.fa-star', true).first();
33301
33302         if(label && icon){
33303             icon.remove();
33304         }
33305         
33306         this.fireEvent('valid', this);
33307     },
33308     
33309     getName: function()
33310     {
33311         return this.name;
33312     }
33313     
33314 });
33315
33316  /**
33317  *
33318  * This is based on 
33319  * http://masonry.desandro.com
33320  *
33321  * The idea is to render all the bricks based on vertical width...
33322  *
33323  * The original code extends 'outlayer' - we might need to use that....
33324  * 
33325  */
33326
33327
33328 /**
33329  * @class Roo.bootstrap.LayoutMasonry
33330  * @extends Roo.bootstrap.Component
33331  * Bootstrap Layout Masonry class
33332  * 
33333  * @constructor
33334  * Create a new Element
33335  * @param {Object} config The config object
33336  */
33337
33338 Roo.bootstrap.LayoutMasonry = function(config){
33339     
33340     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33341     
33342     this.bricks = [];
33343     
33344     Roo.bootstrap.LayoutMasonry.register(this);
33345     
33346     this.addEvents({
33347         // raw events
33348         /**
33349          * @event layout
33350          * Fire after layout the items
33351          * @param {Roo.bootstrap.LayoutMasonry} this
33352          * @param {Roo.EventObject} e
33353          */
33354         "layout" : true
33355     });
33356     
33357 };
33358
33359 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33360     
33361     /**
33362      * @cfg {Boolean} isLayoutInstant = no animation?
33363      */   
33364     isLayoutInstant : false, // needed?
33365    
33366     /**
33367      * @cfg {Number} boxWidth  width of the columns
33368      */   
33369     boxWidth : 450,
33370     
33371       /**
33372      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33373      */   
33374     boxHeight : 0,
33375     
33376     /**
33377      * @cfg {Number} padWidth padding below box..
33378      */   
33379     padWidth : 10, 
33380     
33381     /**
33382      * @cfg {Number} gutter gutter width..
33383      */   
33384     gutter : 10,
33385     
33386      /**
33387      * @cfg {Number} maxCols maximum number of columns
33388      */   
33389     
33390     maxCols: 0,
33391     
33392     /**
33393      * @cfg {Boolean} isAutoInitial defalut true
33394      */   
33395     isAutoInitial : true, 
33396     
33397     containerWidth: 0,
33398     
33399     /**
33400      * @cfg {Boolean} isHorizontal defalut false
33401      */   
33402     isHorizontal : false, 
33403
33404     currentSize : null,
33405     
33406     tag: 'div',
33407     
33408     cls: '',
33409     
33410     bricks: null, //CompositeElement
33411     
33412     cols : 1,
33413     
33414     _isLayoutInited : false,
33415     
33416 //    isAlternative : false, // only use for vertical layout...
33417     
33418     /**
33419      * @cfg {Number} alternativePadWidth padding below box..
33420      */   
33421     alternativePadWidth : 50,
33422     
33423     selectedBrick : [],
33424     
33425     getAutoCreate : function(){
33426         
33427         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33428         
33429         var cfg = {
33430             tag: this.tag,
33431             cls: 'blog-masonary-wrapper ' + this.cls,
33432             cn : {
33433                 cls : 'mas-boxes masonary'
33434             }
33435         };
33436         
33437         return cfg;
33438     },
33439     
33440     getChildContainer: function( )
33441     {
33442         if (this.boxesEl) {
33443             return this.boxesEl;
33444         }
33445         
33446         this.boxesEl = this.el.select('.mas-boxes').first();
33447         
33448         return this.boxesEl;
33449     },
33450     
33451     
33452     initEvents : function()
33453     {
33454         var _this = this;
33455         
33456         if(this.isAutoInitial){
33457             Roo.log('hook children rendered');
33458             this.on('childrenrendered', function() {
33459                 Roo.log('children rendered');
33460                 _this.initial();
33461             } ,this);
33462         }
33463     },
33464     
33465     initial : function()
33466     {
33467         this.selectedBrick = [];
33468         
33469         this.currentSize = this.el.getBox(true);
33470         
33471         Roo.EventManager.onWindowResize(this.resize, this); 
33472
33473         if(!this.isAutoInitial){
33474             this.layout();
33475             return;
33476         }
33477         
33478         this.layout();
33479         
33480         return;
33481         //this.layout.defer(500,this);
33482         
33483     },
33484     
33485     resize : function()
33486     {
33487         var cs = this.el.getBox(true);
33488         
33489         if (
33490                 this.currentSize.width == cs.width && 
33491                 this.currentSize.x == cs.x && 
33492                 this.currentSize.height == cs.height && 
33493                 this.currentSize.y == cs.y 
33494         ) {
33495             Roo.log("no change in with or X or Y");
33496             return;
33497         }
33498         
33499         this.currentSize = cs;
33500         
33501         this.layout();
33502         
33503     },
33504     
33505     layout : function()
33506     {   
33507         this._resetLayout();
33508         
33509         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33510         
33511         this.layoutItems( isInstant );
33512       
33513         this._isLayoutInited = true;
33514         
33515         this.fireEvent('layout', this);
33516         
33517     },
33518     
33519     _resetLayout : function()
33520     {
33521         if(this.isHorizontal){
33522             this.horizontalMeasureColumns();
33523             return;
33524         }
33525         
33526         this.verticalMeasureColumns();
33527         
33528     },
33529     
33530     verticalMeasureColumns : function()
33531     {
33532         this.getContainerWidth();
33533         
33534 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33535 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33536 //            return;
33537 //        }
33538         
33539         var boxWidth = this.boxWidth + this.padWidth;
33540         
33541         if(this.containerWidth < this.boxWidth){
33542             boxWidth = this.containerWidth
33543         }
33544         
33545         var containerWidth = this.containerWidth;
33546         
33547         var cols = Math.floor(containerWidth / boxWidth);
33548         
33549         this.cols = Math.max( cols, 1 );
33550         
33551         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33552         
33553         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33554         
33555         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33556         
33557         this.colWidth = boxWidth + avail - this.padWidth;
33558         
33559         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33560         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33561     },
33562     
33563     horizontalMeasureColumns : function()
33564     {
33565         this.getContainerWidth();
33566         
33567         var boxWidth = this.boxWidth;
33568         
33569         if(this.containerWidth < boxWidth){
33570             boxWidth = this.containerWidth;
33571         }
33572         
33573         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33574         
33575         this.el.setHeight(boxWidth);
33576         
33577     },
33578     
33579     getContainerWidth : function()
33580     {
33581         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33582     },
33583     
33584     layoutItems : function( isInstant )
33585     {
33586         Roo.log(this.bricks);
33587         
33588         var items = Roo.apply([], this.bricks);
33589         
33590         if(this.isHorizontal){
33591             this._horizontalLayoutItems( items , isInstant );
33592             return;
33593         }
33594         
33595 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33596 //            this._verticalAlternativeLayoutItems( items , isInstant );
33597 //            return;
33598 //        }
33599         
33600         this._verticalLayoutItems( items , isInstant );
33601         
33602     },
33603     
33604     _verticalLayoutItems : function ( items , isInstant)
33605     {
33606         if ( !items || !items.length ) {
33607             return;
33608         }
33609         
33610         var standard = [
33611             ['xs', 'xs', 'xs', 'tall'],
33612             ['xs', 'xs', 'tall'],
33613             ['xs', 'xs', 'sm'],
33614             ['xs', 'xs', 'xs'],
33615             ['xs', 'tall'],
33616             ['xs', 'sm'],
33617             ['xs', 'xs'],
33618             ['xs'],
33619             
33620             ['sm', 'xs', 'xs'],
33621             ['sm', 'xs'],
33622             ['sm'],
33623             
33624             ['tall', 'xs', 'xs', 'xs'],
33625             ['tall', 'xs', 'xs'],
33626             ['tall', 'xs'],
33627             ['tall']
33628             
33629         ];
33630         
33631         var queue = [];
33632         
33633         var boxes = [];
33634         
33635         var box = [];
33636         
33637         Roo.each(items, function(item, k){
33638             
33639             switch (item.size) {
33640                 // these layouts take up a full box,
33641                 case 'md' :
33642                 case 'md-left' :
33643                 case 'md-right' :
33644                 case 'wide' :
33645                     
33646                     if(box.length){
33647                         boxes.push(box);
33648                         box = [];
33649                     }
33650                     
33651                     boxes.push([item]);
33652                     
33653                     break;
33654                     
33655                 case 'xs' :
33656                 case 'sm' :
33657                 case 'tall' :
33658                     
33659                     box.push(item);
33660                     
33661                     break;
33662                 default :
33663                     break;
33664                     
33665             }
33666             
33667         }, this);
33668         
33669         if(box.length){
33670             boxes.push(box);
33671             box = [];
33672         }
33673         
33674         var filterPattern = function(box, length)
33675         {
33676             if(!box.length){
33677                 return;
33678             }
33679             
33680             var match = false;
33681             
33682             var pattern = box.slice(0, length);
33683             
33684             var format = [];
33685             
33686             Roo.each(pattern, function(i){
33687                 format.push(i.size);
33688             }, this);
33689             
33690             Roo.each(standard, function(s){
33691                 
33692                 if(String(s) != String(format)){
33693                     return;
33694                 }
33695                 
33696                 match = true;
33697                 return false;
33698                 
33699             }, this);
33700             
33701             if(!match && length == 1){
33702                 return;
33703             }
33704             
33705             if(!match){
33706                 filterPattern(box, length - 1);
33707                 return;
33708             }
33709                 
33710             queue.push(pattern);
33711
33712             box = box.slice(length, box.length);
33713
33714             filterPattern(box, 4);
33715
33716             return;
33717             
33718         }
33719         
33720         Roo.each(boxes, function(box, k){
33721             
33722             if(!box.length){
33723                 return;
33724             }
33725             
33726             if(box.length == 1){
33727                 queue.push(box);
33728                 return;
33729             }
33730             
33731             filterPattern(box, 4);
33732             
33733         }, this);
33734         
33735         this._processVerticalLayoutQueue( queue, isInstant );
33736         
33737     },
33738     
33739 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33740 //    {
33741 //        if ( !items || !items.length ) {
33742 //            return;
33743 //        }
33744 //
33745 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33746 //        
33747 //    },
33748     
33749     _horizontalLayoutItems : function ( items , isInstant)
33750     {
33751         if ( !items || !items.length || items.length < 3) {
33752             return;
33753         }
33754         
33755         items.reverse();
33756         
33757         var eItems = items.slice(0, 3);
33758         
33759         items = items.slice(3, items.length);
33760         
33761         var standard = [
33762             ['xs', 'xs', 'xs', 'wide'],
33763             ['xs', 'xs', 'wide'],
33764             ['xs', 'xs', 'sm'],
33765             ['xs', 'xs', 'xs'],
33766             ['xs', 'wide'],
33767             ['xs', 'sm'],
33768             ['xs', 'xs'],
33769             ['xs'],
33770             
33771             ['sm', 'xs', 'xs'],
33772             ['sm', 'xs'],
33773             ['sm'],
33774             
33775             ['wide', 'xs', 'xs', 'xs'],
33776             ['wide', 'xs', 'xs'],
33777             ['wide', 'xs'],
33778             ['wide'],
33779             
33780             ['wide-thin']
33781         ];
33782         
33783         var queue = [];
33784         
33785         var boxes = [];
33786         
33787         var box = [];
33788         
33789         Roo.each(items, function(item, k){
33790             
33791             switch (item.size) {
33792                 case 'md' :
33793                 case 'md-left' :
33794                 case 'md-right' :
33795                 case 'tall' :
33796                     
33797                     if(box.length){
33798                         boxes.push(box);
33799                         box = [];
33800                     }
33801                     
33802                     boxes.push([item]);
33803                     
33804                     break;
33805                     
33806                 case 'xs' :
33807                 case 'sm' :
33808                 case 'wide' :
33809                 case 'wide-thin' :
33810                     
33811                     box.push(item);
33812                     
33813                     break;
33814                 default :
33815                     break;
33816                     
33817             }
33818             
33819         }, this);
33820         
33821         if(box.length){
33822             boxes.push(box);
33823             box = [];
33824         }
33825         
33826         var filterPattern = function(box, length)
33827         {
33828             if(!box.length){
33829                 return;
33830             }
33831             
33832             var match = false;
33833             
33834             var pattern = box.slice(0, length);
33835             
33836             var format = [];
33837             
33838             Roo.each(pattern, function(i){
33839                 format.push(i.size);
33840             }, this);
33841             
33842             Roo.each(standard, function(s){
33843                 
33844                 if(String(s) != String(format)){
33845                     return;
33846                 }
33847                 
33848                 match = true;
33849                 return false;
33850                 
33851             }, this);
33852             
33853             if(!match && length == 1){
33854                 return;
33855             }
33856             
33857             if(!match){
33858                 filterPattern(box, length - 1);
33859                 return;
33860             }
33861                 
33862             queue.push(pattern);
33863
33864             box = box.slice(length, box.length);
33865
33866             filterPattern(box, 4);
33867
33868             return;
33869             
33870         }
33871         
33872         Roo.each(boxes, function(box, k){
33873             
33874             if(!box.length){
33875                 return;
33876             }
33877             
33878             if(box.length == 1){
33879                 queue.push(box);
33880                 return;
33881             }
33882             
33883             filterPattern(box, 4);
33884             
33885         }, this);
33886         
33887         
33888         var prune = [];
33889         
33890         var pos = this.el.getBox(true);
33891         
33892         var minX = pos.x;
33893         
33894         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33895         
33896         var hit_end = false;
33897         
33898         Roo.each(queue, function(box){
33899             
33900             if(hit_end){
33901                 
33902                 Roo.each(box, function(b){
33903                 
33904                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33905                     b.el.hide();
33906
33907                 }, this);
33908
33909                 return;
33910             }
33911             
33912             var mx = 0;
33913             
33914             Roo.each(box, function(b){
33915                 
33916                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33917                 b.el.show();
33918
33919                 mx = Math.max(mx, b.x);
33920                 
33921             }, this);
33922             
33923             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33924             
33925             if(maxX < minX){
33926                 
33927                 Roo.each(box, function(b){
33928                 
33929                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33930                     b.el.hide();
33931                     
33932                 }, this);
33933                 
33934                 hit_end = true;
33935                 
33936                 return;
33937             }
33938             
33939             prune.push(box);
33940             
33941         }, this);
33942         
33943         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33944     },
33945     
33946     /** Sets position of item in DOM
33947     * @param {Element} item
33948     * @param {Number} x - horizontal position
33949     * @param {Number} y - vertical position
33950     * @param {Boolean} isInstant - disables transitions
33951     */
33952     _processVerticalLayoutQueue : function( queue, isInstant )
33953     {
33954         var pos = this.el.getBox(true);
33955         var x = pos.x;
33956         var y = pos.y;
33957         var maxY = [];
33958         
33959         for (var i = 0; i < this.cols; i++){
33960             maxY[i] = pos.y;
33961         }
33962         
33963         Roo.each(queue, function(box, k){
33964             
33965             var col = k % this.cols;
33966             
33967             Roo.each(box, function(b,kk){
33968                 
33969                 b.el.position('absolute');
33970                 
33971                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33972                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33973                 
33974                 if(b.size == 'md-left' || b.size == 'md-right'){
33975                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33976                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33977                 }
33978                 
33979                 b.el.setWidth(width);
33980                 b.el.setHeight(height);
33981                 // iframe?
33982                 b.el.select('iframe',true).setSize(width,height);
33983                 
33984             }, this);
33985             
33986             for (var i = 0; i < this.cols; i++){
33987                 
33988                 if(maxY[i] < maxY[col]){
33989                     col = i;
33990                     continue;
33991                 }
33992                 
33993                 col = Math.min(col, i);
33994                 
33995             }
33996             
33997             x = pos.x + col * (this.colWidth + this.padWidth);
33998             
33999             y = maxY[col];
34000             
34001             var positions = [];
34002             
34003             switch (box.length){
34004                 case 1 :
34005                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34006                     break;
34007                 case 2 :
34008                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34009                     break;
34010                 case 3 :
34011                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34012                     break;
34013                 case 4 :
34014                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34015                     break;
34016                 default :
34017                     break;
34018             }
34019             
34020             Roo.each(box, function(b,kk){
34021                 
34022                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34023                 
34024                 var sz = b.el.getSize();
34025                 
34026                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34027                 
34028             }, this);
34029             
34030         }, this);
34031         
34032         var mY = 0;
34033         
34034         for (var i = 0; i < this.cols; i++){
34035             mY = Math.max(mY, maxY[i]);
34036         }
34037         
34038         this.el.setHeight(mY - pos.y);
34039         
34040     },
34041     
34042 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34043 //    {
34044 //        var pos = this.el.getBox(true);
34045 //        var x = pos.x;
34046 //        var y = pos.y;
34047 //        var maxX = pos.right;
34048 //        
34049 //        var maxHeight = 0;
34050 //        
34051 //        Roo.each(items, function(item, k){
34052 //            
34053 //            var c = k % 2;
34054 //            
34055 //            item.el.position('absolute');
34056 //                
34057 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34058 //
34059 //            item.el.setWidth(width);
34060 //
34061 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34062 //
34063 //            item.el.setHeight(height);
34064 //            
34065 //            if(c == 0){
34066 //                item.el.setXY([x, y], isInstant ? false : true);
34067 //            } else {
34068 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34069 //            }
34070 //            
34071 //            y = y + height + this.alternativePadWidth;
34072 //            
34073 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34074 //            
34075 //        }, this);
34076 //        
34077 //        this.el.setHeight(maxHeight);
34078 //        
34079 //    },
34080     
34081     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34082     {
34083         var pos = this.el.getBox(true);
34084         
34085         var minX = pos.x;
34086         var minY = pos.y;
34087         
34088         var maxX = pos.right;
34089         
34090         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34091         
34092         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34093         
34094         Roo.each(queue, function(box, k){
34095             
34096             Roo.each(box, function(b, kk){
34097                 
34098                 b.el.position('absolute');
34099                 
34100                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34101                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34102                 
34103                 if(b.size == 'md-left' || b.size == 'md-right'){
34104                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34105                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34106                 }
34107                 
34108                 b.el.setWidth(width);
34109                 b.el.setHeight(height);
34110                 
34111             }, this);
34112             
34113             if(!box.length){
34114                 return;
34115             }
34116             
34117             var positions = [];
34118             
34119             switch (box.length){
34120                 case 1 :
34121                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34122                     break;
34123                 case 2 :
34124                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34125                     break;
34126                 case 3 :
34127                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34128                     break;
34129                 case 4 :
34130                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34131                     break;
34132                 default :
34133                     break;
34134             }
34135             
34136             Roo.each(box, function(b,kk){
34137                 
34138                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34139                 
34140                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34141                 
34142             }, this);
34143             
34144         }, this);
34145         
34146     },
34147     
34148     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34149     {
34150         Roo.each(eItems, function(b,k){
34151             
34152             b.size = (k == 0) ? 'sm' : 'xs';
34153             b.x = (k == 0) ? 2 : 1;
34154             b.y = (k == 0) ? 2 : 1;
34155             
34156             b.el.position('absolute');
34157             
34158             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34159                 
34160             b.el.setWidth(width);
34161             
34162             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34163             
34164             b.el.setHeight(height);
34165             
34166         }, this);
34167
34168         var positions = [];
34169         
34170         positions.push({
34171             x : maxX - this.unitWidth * 2 - this.gutter,
34172             y : minY
34173         });
34174         
34175         positions.push({
34176             x : maxX - this.unitWidth,
34177             y : minY + (this.unitWidth + this.gutter) * 2
34178         });
34179         
34180         positions.push({
34181             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34182             y : minY
34183         });
34184         
34185         Roo.each(eItems, function(b,k){
34186             
34187             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34188
34189         }, this);
34190         
34191     },
34192     
34193     getVerticalOneBoxColPositions : function(x, y, box)
34194     {
34195         var pos = [];
34196         
34197         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34198         
34199         if(box[0].size == 'md-left'){
34200             rand = 0;
34201         }
34202         
34203         if(box[0].size == 'md-right'){
34204             rand = 1;
34205         }
34206         
34207         pos.push({
34208             x : x + (this.unitWidth + this.gutter) * rand,
34209             y : y
34210         });
34211         
34212         return pos;
34213     },
34214     
34215     getVerticalTwoBoxColPositions : function(x, y, box)
34216     {
34217         var pos = [];
34218         
34219         if(box[0].size == 'xs'){
34220             
34221             pos.push({
34222                 x : x,
34223                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34224             });
34225
34226             pos.push({
34227                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34228                 y : y
34229             });
34230             
34231             return pos;
34232             
34233         }
34234         
34235         pos.push({
34236             x : x,
34237             y : y
34238         });
34239
34240         pos.push({
34241             x : x + (this.unitWidth + this.gutter) * 2,
34242             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34243         });
34244         
34245         return pos;
34246         
34247     },
34248     
34249     getVerticalThreeBoxColPositions : function(x, y, box)
34250     {
34251         var pos = [];
34252         
34253         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34254             
34255             pos.push({
34256                 x : x,
34257                 y : y
34258             });
34259
34260             pos.push({
34261                 x : x + (this.unitWidth + this.gutter) * 1,
34262                 y : y
34263             });
34264             
34265             pos.push({
34266                 x : x + (this.unitWidth + this.gutter) * 2,
34267                 y : y
34268             });
34269             
34270             return pos;
34271             
34272         }
34273         
34274         if(box[0].size == 'xs' && box[1].size == 'xs'){
34275             
34276             pos.push({
34277                 x : x,
34278                 y : y
34279             });
34280
34281             pos.push({
34282                 x : x,
34283                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34284             });
34285             
34286             pos.push({
34287                 x : x + (this.unitWidth + this.gutter) * 1,
34288                 y : y
34289             });
34290             
34291             return pos;
34292             
34293         }
34294         
34295         pos.push({
34296             x : x,
34297             y : y
34298         });
34299
34300         pos.push({
34301             x : x + (this.unitWidth + this.gutter) * 2,
34302             y : y
34303         });
34304
34305         pos.push({
34306             x : x + (this.unitWidth + this.gutter) * 2,
34307             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34308         });
34309             
34310         return pos;
34311         
34312     },
34313     
34314     getVerticalFourBoxColPositions : function(x, y, box)
34315     {
34316         var pos = [];
34317         
34318         if(box[0].size == 'xs'){
34319             
34320             pos.push({
34321                 x : x,
34322                 y : y
34323             });
34324
34325             pos.push({
34326                 x : x,
34327                 y : y + (this.unitHeight + this.gutter) * 1
34328             });
34329             
34330             pos.push({
34331                 x : x,
34332                 y : y + (this.unitHeight + this.gutter) * 2
34333             });
34334             
34335             pos.push({
34336                 x : x + (this.unitWidth + this.gutter) * 1,
34337                 y : y
34338             });
34339             
34340             return pos;
34341             
34342         }
34343         
34344         pos.push({
34345             x : x,
34346             y : y
34347         });
34348
34349         pos.push({
34350             x : x + (this.unitWidth + this.gutter) * 2,
34351             y : y
34352         });
34353
34354         pos.push({
34355             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34356             y : y + (this.unitHeight + this.gutter) * 1
34357         });
34358
34359         pos.push({
34360             x : x + (this.unitWidth + this.gutter) * 2,
34361             y : y + (this.unitWidth + this.gutter) * 2
34362         });
34363
34364         return pos;
34365         
34366     },
34367     
34368     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34369     {
34370         var pos = [];
34371         
34372         if(box[0].size == 'md-left'){
34373             pos.push({
34374                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34375                 y : minY
34376             });
34377             
34378             return pos;
34379         }
34380         
34381         if(box[0].size == 'md-right'){
34382             pos.push({
34383                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34384                 y : minY + (this.unitWidth + this.gutter) * 1
34385             });
34386             
34387             return pos;
34388         }
34389         
34390         var rand = Math.floor(Math.random() * (4 - box[0].y));
34391         
34392         pos.push({
34393             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34394             y : minY + (this.unitWidth + this.gutter) * rand
34395         });
34396         
34397         return pos;
34398         
34399     },
34400     
34401     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34402     {
34403         var pos = [];
34404         
34405         if(box[0].size == 'xs'){
34406             
34407             pos.push({
34408                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34409                 y : minY
34410             });
34411
34412             pos.push({
34413                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34414                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34415             });
34416             
34417             return pos;
34418             
34419         }
34420         
34421         pos.push({
34422             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34423             y : minY
34424         });
34425
34426         pos.push({
34427             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34428             y : minY + (this.unitWidth + this.gutter) * 2
34429         });
34430         
34431         return pos;
34432         
34433     },
34434     
34435     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34436     {
34437         var pos = [];
34438         
34439         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34440             
34441             pos.push({
34442                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34443                 y : minY
34444             });
34445
34446             pos.push({
34447                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34448                 y : minY + (this.unitWidth + this.gutter) * 1
34449             });
34450             
34451             pos.push({
34452                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34453                 y : minY + (this.unitWidth + this.gutter) * 2
34454             });
34455             
34456             return pos;
34457             
34458         }
34459         
34460         if(box[0].size == 'xs' && box[1].size == 'xs'){
34461             
34462             pos.push({
34463                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34464                 y : minY
34465             });
34466
34467             pos.push({
34468                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34469                 y : minY
34470             });
34471             
34472             pos.push({
34473                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34474                 y : minY + (this.unitWidth + this.gutter) * 1
34475             });
34476             
34477             return pos;
34478             
34479         }
34480         
34481         pos.push({
34482             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34483             y : minY
34484         });
34485
34486         pos.push({
34487             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34488             y : minY + (this.unitWidth + this.gutter) * 2
34489         });
34490
34491         pos.push({
34492             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34493             y : minY + (this.unitWidth + this.gutter) * 2
34494         });
34495             
34496         return pos;
34497         
34498     },
34499     
34500     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34501     {
34502         var pos = [];
34503         
34504         if(box[0].size == 'xs'){
34505             
34506             pos.push({
34507                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34508                 y : minY
34509             });
34510
34511             pos.push({
34512                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34513                 y : minY
34514             });
34515             
34516             pos.push({
34517                 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),
34518                 y : minY
34519             });
34520             
34521             pos.push({
34522                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34523                 y : minY + (this.unitWidth + this.gutter) * 1
34524             });
34525             
34526             return pos;
34527             
34528         }
34529         
34530         pos.push({
34531             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34532             y : minY
34533         });
34534         
34535         pos.push({
34536             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34537             y : minY + (this.unitWidth + this.gutter) * 2
34538         });
34539         
34540         pos.push({
34541             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34542             y : minY + (this.unitWidth + this.gutter) * 2
34543         });
34544         
34545         pos.push({
34546             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),
34547             y : minY + (this.unitWidth + this.gutter) * 2
34548         });
34549
34550         return pos;
34551         
34552     },
34553     
34554     /**
34555     * remove a Masonry Brick
34556     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34557     */
34558     removeBrick : function(brick_id)
34559     {
34560         if (!brick_id) {
34561             return;
34562         }
34563         
34564         for (var i = 0; i<this.bricks.length; i++) {
34565             if (this.bricks[i].id == brick_id) {
34566                 this.bricks.splice(i,1);
34567                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34568                 this.initial();
34569             }
34570         }
34571     },
34572     
34573     /**
34574     * adds a Masonry Brick
34575     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34576     */
34577     addBrick : function(cfg)
34578     {
34579         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34580         //this.register(cn);
34581         cn.parentId = this.id;
34582         cn.render(this.el);
34583         return cn;
34584     },
34585     
34586     /**
34587     * register a Masonry Brick
34588     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34589     */
34590     
34591     register : function(brick)
34592     {
34593         this.bricks.push(brick);
34594         brick.masonryId = this.id;
34595     },
34596     
34597     /**
34598     * clear all the Masonry Brick
34599     */
34600     clearAll : function()
34601     {
34602         this.bricks = [];
34603         //this.getChildContainer().dom.innerHTML = "";
34604         this.el.dom.innerHTML = '';
34605     },
34606     
34607     getSelected : function()
34608     {
34609         if (!this.selectedBrick) {
34610             return false;
34611         }
34612         
34613         return this.selectedBrick;
34614     }
34615 });
34616
34617 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34618     
34619     groups: {},
34620      /**
34621     * register a Masonry Layout
34622     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34623     */
34624     
34625     register : function(layout)
34626     {
34627         this.groups[layout.id] = layout;
34628     },
34629     /**
34630     * fetch a  Masonry Layout based on the masonry layout ID
34631     * @param {string} the masonry layout to add
34632     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34633     */
34634     
34635     get: function(layout_id) {
34636         if (typeof(this.groups[layout_id]) == 'undefined') {
34637             return false;
34638         }
34639         return this.groups[layout_id] ;
34640     }
34641     
34642     
34643     
34644 });
34645
34646  
34647
34648  /**
34649  *
34650  * This is based on 
34651  * http://masonry.desandro.com
34652  *
34653  * The idea is to render all the bricks based on vertical width...
34654  *
34655  * The original code extends 'outlayer' - we might need to use that....
34656  * 
34657  */
34658
34659
34660 /**
34661  * @class Roo.bootstrap.LayoutMasonryAuto
34662  * @extends Roo.bootstrap.Component
34663  * Bootstrap Layout Masonry class
34664  * 
34665  * @constructor
34666  * Create a new Element
34667  * @param {Object} config The config object
34668  */
34669
34670 Roo.bootstrap.LayoutMasonryAuto = function(config){
34671     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34672 };
34673
34674 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34675     
34676       /**
34677      * @cfg {Boolean} isFitWidth  - resize the width..
34678      */   
34679     isFitWidth : false,  // options..
34680     /**
34681      * @cfg {Boolean} isOriginLeft = left align?
34682      */   
34683     isOriginLeft : true,
34684     /**
34685      * @cfg {Boolean} isOriginTop = top align?
34686      */   
34687     isOriginTop : false,
34688     /**
34689      * @cfg {Boolean} isLayoutInstant = no animation?
34690      */   
34691     isLayoutInstant : false, // needed?
34692     /**
34693      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34694      */   
34695     isResizingContainer : true,
34696     /**
34697      * @cfg {Number} columnWidth  width of the columns 
34698      */   
34699     
34700     columnWidth : 0,
34701     
34702     /**
34703      * @cfg {Number} maxCols maximum number of columns
34704      */   
34705     
34706     maxCols: 0,
34707     /**
34708      * @cfg {Number} padHeight padding below box..
34709      */   
34710     
34711     padHeight : 10, 
34712     
34713     /**
34714      * @cfg {Boolean} isAutoInitial defalut true
34715      */   
34716     
34717     isAutoInitial : true, 
34718     
34719     // private?
34720     gutter : 0,
34721     
34722     containerWidth: 0,
34723     initialColumnWidth : 0,
34724     currentSize : null,
34725     
34726     colYs : null, // array.
34727     maxY : 0,
34728     padWidth: 10,
34729     
34730     
34731     tag: 'div',
34732     cls: '',
34733     bricks: null, //CompositeElement
34734     cols : 0, // array?
34735     // element : null, // wrapped now this.el
34736     _isLayoutInited : null, 
34737     
34738     
34739     getAutoCreate : function(){
34740         
34741         var cfg = {
34742             tag: this.tag,
34743             cls: 'blog-masonary-wrapper ' + this.cls,
34744             cn : {
34745                 cls : 'mas-boxes masonary'
34746             }
34747         };
34748         
34749         return cfg;
34750     },
34751     
34752     getChildContainer: function( )
34753     {
34754         if (this.boxesEl) {
34755             return this.boxesEl;
34756         }
34757         
34758         this.boxesEl = this.el.select('.mas-boxes').first();
34759         
34760         return this.boxesEl;
34761     },
34762     
34763     
34764     initEvents : function()
34765     {
34766         var _this = this;
34767         
34768         if(this.isAutoInitial){
34769             Roo.log('hook children rendered');
34770             this.on('childrenrendered', function() {
34771                 Roo.log('children rendered');
34772                 _this.initial();
34773             } ,this);
34774         }
34775         
34776     },
34777     
34778     initial : function()
34779     {
34780         this.reloadItems();
34781
34782         this.currentSize = this.el.getBox(true);
34783
34784         /// was window resize... - let's see if this works..
34785         Roo.EventManager.onWindowResize(this.resize, this); 
34786
34787         if(!this.isAutoInitial){
34788             this.layout();
34789             return;
34790         }
34791         
34792         this.layout.defer(500,this);
34793     },
34794     
34795     reloadItems: function()
34796     {
34797         this.bricks = this.el.select('.masonry-brick', true);
34798         
34799         this.bricks.each(function(b) {
34800             //Roo.log(b.getSize());
34801             if (!b.attr('originalwidth')) {
34802                 b.attr('originalwidth',  b.getSize().width);
34803             }
34804             
34805         });
34806         
34807         Roo.log(this.bricks.elements.length);
34808     },
34809     
34810     resize : function()
34811     {
34812         Roo.log('resize');
34813         var cs = this.el.getBox(true);
34814         
34815         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34816             Roo.log("no change in with or X");
34817             return;
34818         }
34819         this.currentSize = cs;
34820         this.layout();
34821     },
34822     
34823     layout : function()
34824     {
34825          Roo.log('layout');
34826         this._resetLayout();
34827         //this._manageStamps();
34828       
34829         // don't animate first layout
34830         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34831         this.layoutItems( isInstant );
34832       
34833         // flag for initalized
34834         this._isLayoutInited = true;
34835     },
34836     
34837     layoutItems : function( isInstant )
34838     {
34839         //var items = this._getItemsForLayout( this.items );
34840         // original code supports filtering layout items.. we just ignore it..
34841         
34842         this._layoutItems( this.bricks , isInstant );
34843       
34844         this._postLayout();
34845     },
34846     _layoutItems : function ( items , isInstant)
34847     {
34848        //this.fireEvent( 'layout', this, items );
34849     
34850
34851         if ( !items || !items.elements.length ) {
34852           // no items, emit event with empty array
34853             return;
34854         }
34855
34856         var queue = [];
34857         items.each(function(item) {
34858             Roo.log("layout item");
34859             Roo.log(item);
34860             // get x/y object from method
34861             var position = this._getItemLayoutPosition( item );
34862             // enqueue
34863             position.item = item;
34864             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34865             queue.push( position );
34866         }, this);
34867       
34868         this._processLayoutQueue( queue );
34869     },
34870     /** Sets position of item in DOM
34871     * @param {Element} item
34872     * @param {Number} x - horizontal position
34873     * @param {Number} y - vertical position
34874     * @param {Boolean} isInstant - disables transitions
34875     */
34876     _processLayoutQueue : function( queue )
34877     {
34878         for ( var i=0, len = queue.length; i < len; i++ ) {
34879             var obj = queue[i];
34880             obj.item.position('absolute');
34881             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34882         }
34883     },
34884       
34885     
34886     /**
34887     * Any logic you want to do after each layout,
34888     * i.e. size the container
34889     */
34890     _postLayout : function()
34891     {
34892         this.resizeContainer();
34893     },
34894     
34895     resizeContainer : function()
34896     {
34897         if ( !this.isResizingContainer ) {
34898             return;
34899         }
34900         var size = this._getContainerSize();
34901         if ( size ) {
34902             this.el.setSize(size.width,size.height);
34903             this.boxesEl.setSize(size.width,size.height);
34904         }
34905     },
34906     
34907     
34908     
34909     _resetLayout : function()
34910     {
34911         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34912         this.colWidth = this.el.getWidth();
34913         //this.gutter = this.el.getWidth(); 
34914         
34915         this.measureColumns();
34916
34917         // reset column Y
34918         var i = this.cols;
34919         this.colYs = [];
34920         while (i--) {
34921             this.colYs.push( 0 );
34922         }
34923     
34924         this.maxY = 0;
34925     },
34926
34927     measureColumns : function()
34928     {
34929         this.getContainerWidth();
34930       // if columnWidth is 0, default to outerWidth of first item
34931         if ( !this.columnWidth ) {
34932             var firstItem = this.bricks.first();
34933             Roo.log(firstItem);
34934             this.columnWidth  = this.containerWidth;
34935             if (firstItem && firstItem.attr('originalwidth') ) {
34936                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34937             }
34938             // columnWidth fall back to item of first element
34939             Roo.log("set column width?");
34940                         this.initialColumnWidth = this.columnWidth  ;
34941
34942             // if first elem has no width, default to size of container
34943             
34944         }
34945         
34946         
34947         if (this.initialColumnWidth) {
34948             this.columnWidth = this.initialColumnWidth;
34949         }
34950         
34951         
34952             
34953         // column width is fixed at the top - however if container width get's smaller we should
34954         // reduce it...
34955         
34956         // this bit calcs how man columns..
34957             
34958         var columnWidth = this.columnWidth += this.gutter;
34959       
34960         // calculate columns
34961         var containerWidth = this.containerWidth + this.gutter;
34962         
34963         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34964         // fix rounding errors, typically with gutters
34965         var excess = columnWidth - containerWidth % columnWidth;
34966         
34967         
34968         // if overshoot is less than a pixel, round up, otherwise floor it
34969         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34970         cols = Math[ mathMethod ]( cols );
34971         this.cols = Math.max( cols, 1 );
34972         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34973         
34974          // padding positioning..
34975         var totalColWidth = this.cols * this.columnWidth;
34976         var padavail = this.containerWidth - totalColWidth;
34977         // so for 2 columns - we need 3 'pads'
34978         
34979         var padNeeded = (1+this.cols) * this.padWidth;
34980         
34981         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34982         
34983         this.columnWidth += padExtra
34984         //this.padWidth = Math.floor(padavail /  ( this.cols));
34985         
34986         // adjust colum width so that padding is fixed??
34987         
34988         // we have 3 columns ... total = width * 3
34989         // we have X left over... that should be used by 
34990         
34991         //if (this.expandC) {
34992             
34993         //}
34994         
34995         
34996         
34997     },
34998     
34999     getContainerWidth : function()
35000     {
35001        /* // container is parent if fit width
35002         var container = this.isFitWidth ? this.element.parentNode : this.element;
35003         // check that this.size and size are there
35004         // IE8 triggers resize on body size change, so they might not be
35005         
35006         var size = getSize( container );  //FIXME
35007         this.containerWidth = size && size.innerWidth; //FIXME
35008         */
35009          
35010         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35011         
35012     },
35013     
35014     _getItemLayoutPosition : function( item )  // what is item?
35015     {
35016         // we resize the item to our columnWidth..
35017       
35018         item.setWidth(this.columnWidth);
35019         item.autoBoxAdjust  = false;
35020         
35021         var sz = item.getSize();
35022  
35023         // how many columns does this brick span
35024         var remainder = this.containerWidth % this.columnWidth;
35025         
35026         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35027         // round if off by 1 pixel, otherwise use ceil
35028         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35029         colSpan = Math.min( colSpan, this.cols );
35030         
35031         // normally this should be '1' as we dont' currently allow multi width columns..
35032         
35033         var colGroup = this._getColGroup( colSpan );
35034         // get the minimum Y value from the columns
35035         var minimumY = Math.min.apply( Math, colGroup );
35036         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35037         
35038         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35039          
35040         // position the brick
35041         var position = {
35042             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35043             y: this.currentSize.y + minimumY + this.padHeight
35044         };
35045         
35046         Roo.log(position);
35047         // apply setHeight to necessary columns
35048         var setHeight = minimumY + sz.height + this.padHeight;
35049         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35050         
35051         var setSpan = this.cols + 1 - colGroup.length;
35052         for ( var i = 0; i < setSpan; i++ ) {
35053           this.colYs[ shortColIndex + i ] = setHeight ;
35054         }
35055       
35056         return position;
35057     },
35058     
35059     /**
35060      * @param {Number} colSpan - number of columns the element spans
35061      * @returns {Array} colGroup
35062      */
35063     _getColGroup : function( colSpan )
35064     {
35065         if ( colSpan < 2 ) {
35066           // if brick spans only one column, use all the column Ys
35067           return this.colYs;
35068         }
35069       
35070         var colGroup = [];
35071         // how many different places could this brick fit horizontally
35072         var groupCount = this.cols + 1 - colSpan;
35073         // for each group potential horizontal position
35074         for ( var i = 0; i < groupCount; i++ ) {
35075           // make an array of colY values for that one group
35076           var groupColYs = this.colYs.slice( i, i + colSpan );
35077           // and get the max value of the array
35078           colGroup[i] = Math.max.apply( Math, groupColYs );
35079         }
35080         return colGroup;
35081     },
35082     /*
35083     _manageStamp : function( stamp )
35084     {
35085         var stampSize =  stamp.getSize();
35086         var offset = stamp.getBox();
35087         // get the columns that this stamp affects
35088         var firstX = this.isOriginLeft ? offset.x : offset.right;
35089         var lastX = firstX + stampSize.width;
35090         var firstCol = Math.floor( firstX / this.columnWidth );
35091         firstCol = Math.max( 0, firstCol );
35092         
35093         var lastCol = Math.floor( lastX / this.columnWidth );
35094         // lastCol should not go over if multiple of columnWidth #425
35095         lastCol -= lastX % this.columnWidth ? 0 : 1;
35096         lastCol = Math.min( this.cols - 1, lastCol );
35097         
35098         // set colYs to bottom of the stamp
35099         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35100             stampSize.height;
35101             
35102         for ( var i = firstCol; i <= lastCol; i++ ) {
35103           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35104         }
35105     },
35106     */
35107     
35108     _getContainerSize : function()
35109     {
35110         this.maxY = Math.max.apply( Math, this.colYs );
35111         var size = {
35112             height: this.maxY
35113         };
35114       
35115         if ( this.isFitWidth ) {
35116             size.width = this._getContainerFitWidth();
35117         }
35118       
35119         return size;
35120     },
35121     
35122     _getContainerFitWidth : function()
35123     {
35124         var unusedCols = 0;
35125         // count unused columns
35126         var i = this.cols;
35127         while ( --i ) {
35128           if ( this.colYs[i] !== 0 ) {
35129             break;
35130           }
35131           unusedCols++;
35132         }
35133         // fit container to columns that have been used
35134         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35135     },
35136     
35137     needsResizeLayout : function()
35138     {
35139         var previousWidth = this.containerWidth;
35140         this.getContainerWidth();
35141         return previousWidth !== this.containerWidth;
35142     }
35143  
35144 });
35145
35146  
35147
35148  /*
35149  * - LGPL
35150  *
35151  * element
35152  * 
35153  */
35154
35155 /**
35156  * @class Roo.bootstrap.MasonryBrick
35157  * @extends Roo.bootstrap.Component
35158  * Bootstrap MasonryBrick class
35159  * 
35160  * @constructor
35161  * Create a new MasonryBrick
35162  * @param {Object} config The config object
35163  */
35164
35165 Roo.bootstrap.MasonryBrick = function(config){
35166     
35167     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35168     
35169     Roo.bootstrap.MasonryBrick.register(this);
35170     
35171     this.addEvents({
35172         // raw events
35173         /**
35174          * @event click
35175          * When a MasonryBrick is clcik
35176          * @param {Roo.bootstrap.MasonryBrick} this
35177          * @param {Roo.EventObject} e
35178          */
35179         "click" : true
35180     });
35181 };
35182
35183 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35184     
35185     /**
35186      * @cfg {String} title
35187      */   
35188     title : '',
35189     /**
35190      * @cfg {String} html
35191      */   
35192     html : '',
35193     /**
35194      * @cfg {String} bgimage
35195      */   
35196     bgimage : '',
35197     /**
35198      * @cfg {String} videourl
35199      */   
35200     videourl : '',
35201     /**
35202      * @cfg {String} cls
35203      */   
35204     cls : '',
35205     /**
35206      * @cfg {String} href
35207      */   
35208     href : '',
35209     /**
35210      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35211      */   
35212     size : 'xs',
35213     
35214     /**
35215      * @cfg {String} placetitle (center|bottom)
35216      */   
35217     placetitle : '',
35218     
35219     /**
35220      * @cfg {Boolean} isFitContainer defalut true
35221      */   
35222     isFitContainer : true, 
35223     
35224     /**
35225      * @cfg {Boolean} preventDefault defalut false
35226      */   
35227     preventDefault : false, 
35228     
35229     /**
35230      * @cfg {Boolean} inverse defalut false
35231      */   
35232     maskInverse : false, 
35233     
35234     getAutoCreate : function()
35235     {
35236         if(!this.isFitContainer){
35237             return this.getSplitAutoCreate();
35238         }
35239         
35240         var cls = 'masonry-brick masonry-brick-full';
35241         
35242         if(this.href.length){
35243             cls += ' masonry-brick-link';
35244         }
35245         
35246         if(this.bgimage.length){
35247             cls += ' masonry-brick-image';
35248         }
35249         
35250         if(this.maskInverse){
35251             cls += ' mask-inverse';
35252         }
35253         
35254         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35255             cls += ' enable-mask';
35256         }
35257         
35258         if(this.size){
35259             cls += ' masonry-' + this.size + '-brick';
35260         }
35261         
35262         if(this.placetitle.length){
35263             
35264             switch (this.placetitle) {
35265                 case 'center' :
35266                     cls += ' masonry-center-title';
35267                     break;
35268                 case 'bottom' :
35269                     cls += ' masonry-bottom-title';
35270                     break;
35271                 default:
35272                     break;
35273             }
35274             
35275         } else {
35276             if(!this.html.length && !this.bgimage.length){
35277                 cls += ' masonry-center-title';
35278             }
35279
35280             if(!this.html.length && this.bgimage.length){
35281                 cls += ' masonry-bottom-title';
35282             }
35283         }
35284         
35285         if(this.cls){
35286             cls += ' ' + this.cls;
35287         }
35288         
35289         var cfg = {
35290             tag: (this.href.length) ? 'a' : 'div',
35291             cls: cls,
35292             cn: [
35293                 {
35294                     tag: 'div',
35295                     cls: 'masonry-brick-mask'
35296                 },
35297                 {
35298                     tag: 'div',
35299                     cls: 'masonry-brick-paragraph',
35300                     cn: []
35301                 }
35302             ]
35303         };
35304         
35305         if(this.href.length){
35306             cfg.href = this.href;
35307         }
35308         
35309         var cn = cfg.cn[1].cn;
35310         
35311         if(this.title.length){
35312             cn.push({
35313                 tag: 'h4',
35314                 cls: 'masonry-brick-title',
35315                 html: this.title
35316             });
35317         }
35318         
35319         if(this.html.length){
35320             cn.push({
35321                 tag: 'p',
35322                 cls: 'masonry-brick-text',
35323                 html: this.html
35324             });
35325         }
35326         
35327         if (!this.title.length && !this.html.length) {
35328             cfg.cn[1].cls += ' hide';
35329         }
35330         
35331         if(this.bgimage.length){
35332             cfg.cn.push({
35333                 tag: 'img',
35334                 cls: 'masonry-brick-image-view',
35335                 src: this.bgimage
35336             });
35337         }
35338         
35339         if(this.videourl.length){
35340             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35341             // youtube support only?
35342             cfg.cn.push({
35343                 tag: 'iframe',
35344                 cls: 'masonry-brick-image-view',
35345                 src: vurl,
35346                 frameborder : 0,
35347                 allowfullscreen : true
35348             });
35349         }
35350         
35351         return cfg;
35352         
35353     },
35354     
35355     getSplitAutoCreate : function()
35356     {
35357         var cls = 'masonry-brick masonry-brick-split';
35358         
35359         if(this.href.length){
35360             cls += ' masonry-brick-link';
35361         }
35362         
35363         if(this.bgimage.length){
35364             cls += ' masonry-brick-image';
35365         }
35366         
35367         if(this.size){
35368             cls += ' masonry-' + this.size + '-brick';
35369         }
35370         
35371         switch (this.placetitle) {
35372             case 'center' :
35373                 cls += ' masonry-center-title';
35374                 break;
35375             case 'bottom' :
35376                 cls += ' masonry-bottom-title';
35377                 break;
35378             default:
35379                 if(!this.bgimage.length){
35380                     cls += ' masonry-center-title';
35381                 }
35382
35383                 if(this.bgimage.length){
35384                     cls += ' masonry-bottom-title';
35385                 }
35386                 break;
35387         }
35388         
35389         if(this.cls){
35390             cls += ' ' + this.cls;
35391         }
35392         
35393         var cfg = {
35394             tag: (this.href.length) ? 'a' : 'div',
35395             cls: cls,
35396             cn: [
35397                 {
35398                     tag: 'div',
35399                     cls: 'masonry-brick-split-head',
35400                     cn: [
35401                         {
35402                             tag: 'div',
35403                             cls: 'masonry-brick-paragraph',
35404                             cn: []
35405                         }
35406                     ]
35407                 },
35408                 {
35409                     tag: 'div',
35410                     cls: 'masonry-brick-split-body',
35411                     cn: []
35412                 }
35413             ]
35414         };
35415         
35416         if(this.href.length){
35417             cfg.href = this.href;
35418         }
35419         
35420         if(this.title.length){
35421             cfg.cn[0].cn[0].cn.push({
35422                 tag: 'h4',
35423                 cls: 'masonry-brick-title',
35424                 html: this.title
35425             });
35426         }
35427         
35428         if(this.html.length){
35429             cfg.cn[1].cn.push({
35430                 tag: 'p',
35431                 cls: 'masonry-brick-text',
35432                 html: this.html
35433             });
35434         }
35435
35436         if(this.bgimage.length){
35437             cfg.cn[0].cn.push({
35438                 tag: 'img',
35439                 cls: 'masonry-brick-image-view',
35440                 src: this.bgimage
35441             });
35442         }
35443         
35444         if(this.videourl.length){
35445             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35446             // youtube support only?
35447             cfg.cn[0].cn.cn.push({
35448                 tag: 'iframe',
35449                 cls: 'masonry-brick-image-view',
35450                 src: vurl,
35451                 frameborder : 0,
35452                 allowfullscreen : true
35453             });
35454         }
35455         
35456         return cfg;
35457     },
35458     
35459     initEvents: function() 
35460     {
35461         switch (this.size) {
35462             case 'xs' :
35463                 this.x = 1;
35464                 this.y = 1;
35465                 break;
35466             case 'sm' :
35467                 this.x = 2;
35468                 this.y = 2;
35469                 break;
35470             case 'md' :
35471             case 'md-left' :
35472             case 'md-right' :
35473                 this.x = 3;
35474                 this.y = 3;
35475                 break;
35476             case 'tall' :
35477                 this.x = 2;
35478                 this.y = 3;
35479                 break;
35480             case 'wide' :
35481                 this.x = 3;
35482                 this.y = 2;
35483                 break;
35484             case 'wide-thin' :
35485                 this.x = 3;
35486                 this.y = 1;
35487                 break;
35488                         
35489             default :
35490                 break;
35491         }
35492         
35493         if(Roo.isTouch){
35494             this.el.on('touchstart', this.onTouchStart, this);
35495             this.el.on('touchmove', this.onTouchMove, this);
35496             this.el.on('touchend', this.onTouchEnd, this);
35497             this.el.on('contextmenu', this.onContextMenu, this);
35498         } else {
35499             this.el.on('mouseenter'  ,this.enter, this);
35500             this.el.on('mouseleave', this.leave, this);
35501             this.el.on('click', this.onClick, this);
35502         }
35503         
35504         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35505             this.parent().bricks.push(this);   
35506         }
35507         
35508     },
35509     
35510     onClick: function(e, el)
35511     {
35512         var time = this.endTimer - this.startTimer;
35513         // Roo.log(e.preventDefault());
35514         if(Roo.isTouch){
35515             if(time > 1000){
35516                 e.preventDefault();
35517                 return;
35518             }
35519         }
35520         
35521         if(!this.preventDefault){
35522             return;
35523         }
35524         
35525         e.preventDefault();
35526         
35527         if (this.activeClass != '') {
35528             this.selectBrick();
35529         }
35530         
35531         this.fireEvent('click', this, e);
35532     },
35533     
35534     enter: function(e, el)
35535     {
35536         e.preventDefault();
35537         
35538         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35539             return;
35540         }
35541         
35542         if(this.bgimage.length && this.html.length){
35543             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35544         }
35545     },
35546     
35547     leave: function(e, el)
35548     {
35549         e.preventDefault();
35550         
35551         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35552             return;
35553         }
35554         
35555         if(this.bgimage.length && this.html.length){
35556             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35557         }
35558     },
35559     
35560     onTouchStart: function(e, el)
35561     {
35562 //        e.preventDefault();
35563         
35564         this.touchmoved = false;
35565         
35566         if(!this.isFitContainer){
35567             return;
35568         }
35569         
35570         if(!this.bgimage.length || !this.html.length){
35571             return;
35572         }
35573         
35574         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35575         
35576         this.timer = new Date().getTime();
35577         
35578     },
35579     
35580     onTouchMove: function(e, el)
35581     {
35582         this.touchmoved = true;
35583     },
35584     
35585     onContextMenu : function(e,el)
35586     {
35587         e.preventDefault();
35588         e.stopPropagation();
35589         return false;
35590     },
35591     
35592     onTouchEnd: function(e, el)
35593     {
35594 //        e.preventDefault();
35595         
35596         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35597         
35598             this.leave(e,el);
35599             
35600             return;
35601         }
35602         
35603         if(!this.bgimage.length || !this.html.length){
35604             
35605             if(this.href.length){
35606                 window.location.href = this.href;
35607             }
35608             
35609             return;
35610         }
35611         
35612         if(!this.isFitContainer){
35613             return;
35614         }
35615         
35616         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35617         
35618         window.location.href = this.href;
35619     },
35620     
35621     //selection on single brick only
35622     selectBrick : function() {
35623         
35624         if (!this.parentId) {
35625             return;
35626         }
35627         
35628         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35629         var index = m.selectedBrick.indexOf(this.id);
35630         
35631         if ( index > -1) {
35632             m.selectedBrick.splice(index,1);
35633             this.el.removeClass(this.activeClass);
35634             return;
35635         }
35636         
35637         for(var i = 0; i < m.selectedBrick.length; i++) {
35638             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35639             b.el.removeClass(b.activeClass);
35640         }
35641         
35642         m.selectedBrick = [];
35643         
35644         m.selectedBrick.push(this.id);
35645         this.el.addClass(this.activeClass);
35646         return;
35647     },
35648     
35649     isSelected : function(){
35650         return this.el.hasClass(this.activeClass);
35651         
35652     }
35653 });
35654
35655 Roo.apply(Roo.bootstrap.MasonryBrick, {
35656     
35657     //groups: {},
35658     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35659      /**
35660     * register a Masonry Brick
35661     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35662     */
35663     
35664     register : function(brick)
35665     {
35666         //this.groups[brick.id] = brick;
35667         this.groups.add(brick.id, brick);
35668     },
35669     /**
35670     * fetch a  masonry brick based on the masonry brick ID
35671     * @param {string} the masonry brick to add
35672     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35673     */
35674     
35675     get: function(brick_id) 
35676     {
35677         // if (typeof(this.groups[brick_id]) == 'undefined') {
35678         //     return false;
35679         // }
35680         // return this.groups[brick_id] ;
35681         
35682         if(this.groups.key(brick_id)) {
35683             return this.groups.key(brick_id);
35684         }
35685         
35686         return false;
35687     }
35688     
35689     
35690     
35691 });
35692
35693  /*
35694  * - LGPL
35695  *
35696  * element
35697  * 
35698  */
35699
35700 /**
35701  * @class Roo.bootstrap.Brick
35702  * @extends Roo.bootstrap.Component
35703  * Bootstrap Brick class
35704  * 
35705  * @constructor
35706  * Create a new Brick
35707  * @param {Object} config The config object
35708  */
35709
35710 Roo.bootstrap.Brick = function(config){
35711     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35712     
35713     this.addEvents({
35714         // raw events
35715         /**
35716          * @event click
35717          * When a Brick is click
35718          * @param {Roo.bootstrap.Brick} this
35719          * @param {Roo.EventObject} e
35720          */
35721         "click" : true
35722     });
35723 };
35724
35725 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35726     
35727     /**
35728      * @cfg {String} title
35729      */   
35730     title : '',
35731     /**
35732      * @cfg {String} html
35733      */   
35734     html : '',
35735     /**
35736      * @cfg {String} bgimage
35737      */   
35738     bgimage : '',
35739     /**
35740      * @cfg {String} cls
35741      */   
35742     cls : '',
35743     /**
35744      * @cfg {String} href
35745      */   
35746     href : '',
35747     /**
35748      * @cfg {String} video
35749      */   
35750     video : '',
35751     /**
35752      * @cfg {Boolean} square
35753      */   
35754     square : true,
35755     
35756     getAutoCreate : function()
35757     {
35758         var cls = 'roo-brick';
35759         
35760         if(this.href.length){
35761             cls += ' roo-brick-link';
35762         }
35763         
35764         if(this.bgimage.length){
35765             cls += ' roo-brick-image';
35766         }
35767         
35768         if(!this.html.length && !this.bgimage.length){
35769             cls += ' roo-brick-center-title';
35770         }
35771         
35772         if(!this.html.length && this.bgimage.length){
35773             cls += ' roo-brick-bottom-title';
35774         }
35775         
35776         if(this.cls){
35777             cls += ' ' + this.cls;
35778         }
35779         
35780         var cfg = {
35781             tag: (this.href.length) ? 'a' : 'div',
35782             cls: cls,
35783             cn: [
35784                 {
35785                     tag: 'div',
35786                     cls: 'roo-brick-paragraph',
35787                     cn: []
35788                 }
35789             ]
35790         };
35791         
35792         if(this.href.length){
35793             cfg.href = this.href;
35794         }
35795         
35796         var cn = cfg.cn[0].cn;
35797         
35798         if(this.title.length){
35799             cn.push({
35800                 tag: 'h4',
35801                 cls: 'roo-brick-title',
35802                 html: this.title
35803             });
35804         }
35805         
35806         if(this.html.length){
35807             cn.push({
35808                 tag: 'p',
35809                 cls: 'roo-brick-text',
35810                 html: this.html
35811             });
35812         } else {
35813             cn.cls += ' hide';
35814         }
35815         
35816         if(this.bgimage.length){
35817             cfg.cn.push({
35818                 tag: 'img',
35819                 cls: 'roo-brick-image-view',
35820                 src: this.bgimage
35821             });
35822         }
35823         
35824         return cfg;
35825     },
35826     
35827     initEvents: function() 
35828     {
35829         if(this.title.length || this.html.length){
35830             this.el.on('mouseenter'  ,this.enter, this);
35831             this.el.on('mouseleave', this.leave, this);
35832         }
35833         
35834         Roo.EventManager.onWindowResize(this.resize, this); 
35835         
35836         if(this.bgimage.length){
35837             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35838             this.imageEl.on('load', this.onImageLoad, this);
35839             return;
35840         }
35841         
35842         this.resize();
35843     },
35844     
35845     onImageLoad : function()
35846     {
35847         this.resize();
35848     },
35849     
35850     resize : function()
35851     {
35852         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35853         
35854         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35855         
35856         if(this.bgimage.length){
35857             var image = this.el.select('.roo-brick-image-view', true).first();
35858             
35859             image.setWidth(paragraph.getWidth());
35860             
35861             if(this.square){
35862                 image.setHeight(paragraph.getWidth());
35863             }
35864             
35865             this.el.setHeight(image.getHeight());
35866             paragraph.setHeight(image.getHeight());
35867             
35868         }
35869         
35870     },
35871     
35872     enter: function(e, el)
35873     {
35874         e.preventDefault();
35875         
35876         if(this.bgimage.length){
35877             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35878             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35879         }
35880     },
35881     
35882     leave: function(e, el)
35883     {
35884         e.preventDefault();
35885         
35886         if(this.bgimage.length){
35887             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35888             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35889         }
35890     }
35891     
35892 });
35893
35894  
35895
35896  /*
35897  * - LGPL
35898  *
35899  * Number field 
35900  */
35901
35902 /**
35903  * @class Roo.bootstrap.NumberField
35904  * @extends Roo.bootstrap.Input
35905  * Bootstrap NumberField class
35906  * 
35907  * 
35908  * 
35909  * 
35910  * @constructor
35911  * Create a new NumberField
35912  * @param {Object} config The config object
35913  */
35914
35915 Roo.bootstrap.NumberField = function(config){
35916     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35917 };
35918
35919 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35920     
35921     /**
35922      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35923      */
35924     allowDecimals : true,
35925     /**
35926      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35927      */
35928     decimalSeparator : ".",
35929     /**
35930      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35931      */
35932     decimalPrecision : 2,
35933     /**
35934      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35935      */
35936     allowNegative : true,
35937     
35938     /**
35939      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35940      */
35941     allowZero: true,
35942     /**
35943      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35944      */
35945     minValue : Number.NEGATIVE_INFINITY,
35946     /**
35947      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35948      */
35949     maxValue : Number.MAX_VALUE,
35950     /**
35951      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35952      */
35953     minText : "The minimum value for this field is {0}",
35954     /**
35955      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35956      */
35957     maxText : "The maximum value for this field is {0}",
35958     /**
35959      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35960      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35961      */
35962     nanText : "{0} is not a valid number",
35963     /**
35964      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35965      */
35966     thousandsDelimiter : false,
35967     /**
35968      * @cfg {String} valueAlign alignment of value
35969      */
35970     valueAlign : "left",
35971
35972     getAutoCreate : function()
35973     {
35974         var hiddenInput = {
35975             tag: 'input',
35976             type: 'hidden',
35977             id: Roo.id(),
35978             cls: 'hidden-number-input'
35979         };
35980         
35981         if (this.name) {
35982             hiddenInput.name = this.name;
35983         }
35984         
35985         this.name = '';
35986         
35987         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35988         
35989         this.name = hiddenInput.name;
35990         
35991         if(cfg.cn.length > 0) {
35992             cfg.cn.push(hiddenInput);
35993         }
35994         
35995         return cfg;
35996     },
35997
35998     // private
35999     initEvents : function()
36000     {   
36001         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36002         
36003         var allowed = "0123456789";
36004         
36005         if(this.allowDecimals){
36006             allowed += this.decimalSeparator;
36007         }
36008         
36009         if(this.allowNegative){
36010             allowed += "-";
36011         }
36012         
36013         if(this.thousandsDelimiter) {
36014             allowed += ",";
36015         }
36016         
36017         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36018         
36019         var keyPress = function(e){
36020             
36021             var k = e.getKey();
36022             
36023             var c = e.getCharCode();
36024             
36025             if(
36026                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36027                     allowed.indexOf(String.fromCharCode(c)) === -1
36028             ){
36029                 e.stopEvent();
36030                 return;
36031             }
36032             
36033             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36034                 return;
36035             }
36036             
36037             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36038                 e.stopEvent();
36039             }
36040         };
36041         
36042         this.el.on("keypress", keyPress, this);
36043     },
36044     
36045     validateValue : function(value)
36046     {
36047         
36048         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36049             return false;
36050         }
36051         
36052         var num = this.parseValue(value);
36053         
36054         if(isNaN(num)){
36055             this.markInvalid(String.format(this.nanText, value));
36056             return false;
36057         }
36058         
36059         if(num < this.minValue){
36060             this.markInvalid(String.format(this.minText, this.minValue));
36061             return false;
36062         }
36063         
36064         if(num > this.maxValue){
36065             this.markInvalid(String.format(this.maxText, this.maxValue));
36066             return false;
36067         }
36068         
36069         return true;
36070     },
36071
36072     getValue : function()
36073     {
36074         var v = this.hiddenEl().getValue();
36075         
36076         return this.fixPrecision(this.parseValue(v));
36077     },
36078
36079     parseValue : function(value)
36080     {
36081         if(this.thousandsDelimiter) {
36082             value += "";
36083             r = new RegExp(",", "g");
36084             value = value.replace(r, "");
36085         }
36086         
36087         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36088         return isNaN(value) ? '' : value;
36089     },
36090
36091     fixPrecision : function(value)
36092     {
36093         if(this.thousandsDelimiter) {
36094             value += "";
36095             r = new RegExp(",", "g");
36096             value = value.replace(r, "");
36097         }
36098         
36099         var nan = isNaN(value);
36100         
36101         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36102             return nan ? '' : value;
36103         }
36104         return parseFloat(value).toFixed(this.decimalPrecision);
36105     },
36106
36107     setValue : function(v)
36108     {
36109         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36110         
36111         this.value = v;
36112         
36113         if(this.rendered){
36114             
36115             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36116             
36117             this.inputEl().dom.value = (v == '') ? '' :
36118                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36119             
36120             if(!this.allowZero && v === '0') {
36121                 this.hiddenEl().dom.value = '';
36122                 this.inputEl().dom.value = '';
36123             }
36124             
36125             this.validate();
36126         }
36127     },
36128
36129     decimalPrecisionFcn : function(v)
36130     {
36131         return Math.floor(v);
36132     },
36133
36134     beforeBlur : function()
36135     {
36136         var v = this.parseValue(this.getRawValue());
36137         
36138         if(v || v === 0 || v === ''){
36139             this.setValue(v);
36140         }
36141     },
36142     
36143     hiddenEl : function()
36144     {
36145         return this.el.select('input.hidden-number-input',true).first();
36146     }
36147     
36148 });
36149
36150  
36151
36152 /*
36153 * Licence: LGPL
36154 */
36155
36156 /**
36157  * @class Roo.bootstrap.DocumentSlider
36158  * @extends Roo.bootstrap.Component
36159  * Bootstrap DocumentSlider class
36160  * 
36161  * @constructor
36162  * Create a new DocumentViewer
36163  * @param {Object} config The config object
36164  */
36165
36166 Roo.bootstrap.DocumentSlider = function(config){
36167     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36168     
36169     this.files = [];
36170     
36171     this.addEvents({
36172         /**
36173          * @event initial
36174          * Fire after initEvent
36175          * @param {Roo.bootstrap.DocumentSlider} this
36176          */
36177         "initial" : true,
36178         /**
36179          * @event update
36180          * Fire after update
36181          * @param {Roo.bootstrap.DocumentSlider} this
36182          */
36183         "update" : true,
36184         /**
36185          * @event click
36186          * Fire after click
36187          * @param {Roo.bootstrap.DocumentSlider} this
36188          */
36189         "click" : true
36190     });
36191 };
36192
36193 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36194     
36195     files : false,
36196     
36197     indicator : 0,
36198     
36199     getAutoCreate : function()
36200     {
36201         var cfg = {
36202             tag : 'div',
36203             cls : 'roo-document-slider',
36204             cn : [
36205                 {
36206                     tag : 'div',
36207                     cls : 'roo-document-slider-header',
36208                     cn : [
36209                         {
36210                             tag : 'div',
36211                             cls : 'roo-document-slider-header-title'
36212                         }
36213                     ]
36214                 },
36215                 {
36216                     tag : 'div',
36217                     cls : 'roo-document-slider-body',
36218                     cn : [
36219                         {
36220                             tag : 'div',
36221                             cls : 'roo-document-slider-prev',
36222                             cn : [
36223                                 {
36224                                     tag : 'i',
36225                                     cls : 'fa fa-chevron-left'
36226                                 }
36227                             ]
36228                         },
36229                         {
36230                             tag : 'div',
36231                             cls : 'roo-document-slider-thumb',
36232                             cn : [
36233                                 {
36234                                     tag : 'img',
36235                                     cls : 'roo-document-slider-image'
36236                                 }
36237                             ]
36238                         },
36239                         {
36240                             tag : 'div',
36241                             cls : 'roo-document-slider-next',
36242                             cn : [
36243                                 {
36244                                     tag : 'i',
36245                                     cls : 'fa fa-chevron-right'
36246                                 }
36247                             ]
36248                         }
36249                     ]
36250                 }
36251             ]
36252         };
36253         
36254         return cfg;
36255     },
36256     
36257     initEvents : function()
36258     {
36259         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36260         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36261         
36262         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36263         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36264         
36265         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36266         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36267         
36268         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36269         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36270         
36271         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36272         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36273         
36274         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36275         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36276         
36277         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36278         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36279         
36280         this.thumbEl.on('click', this.onClick, this);
36281         
36282         this.prevIndicator.on('click', this.prev, this);
36283         
36284         this.nextIndicator.on('click', this.next, this);
36285         
36286     },
36287     
36288     initial : function()
36289     {
36290         if(this.files.length){
36291             this.indicator = 1;
36292             this.update()
36293         }
36294         
36295         this.fireEvent('initial', this);
36296     },
36297     
36298     update : function()
36299     {
36300         this.imageEl.attr('src', this.files[this.indicator - 1]);
36301         
36302         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36303         
36304         this.prevIndicator.show();
36305         
36306         if(this.indicator == 1){
36307             this.prevIndicator.hide();
36308         }
36309         
36310         this.nextIndicator.show();
36311         
36312         if(this.indicator == this.files.length){
36313             this.nextIndicator.hide();
36314         }
36315         
36316         this.thumbEl.scrollTo('top');
36317         
36318         this.fireEvent('update', this);
36319     },
36320     
36321     onClick : function(e)
36322     {
36323         e.preventDefault();
36324         
36325         this.fireEvent('click', this);
36326     },
36327     
36328     prev : function(e)
36329     {
36330         e.preventDefault();
36331         
36332         this.indicator = Math.max(1, this.indicator - 1);
36333         
36334         this.update();
36335     },
36336     
36337     next : function(e)
36338     {
36339         e.preventDefault();
36340         
36341         this.indicator = Math.min(this.files.length, this.indicator + 1);
36342         
36343         this.update();
36344     }
36345 });
36346 /*
36347  * - LGPL
36348  *
36349  * RadioSet
36350  *
36351  *
36352  */
36353
36354 /**
36355  * @class Roo.bootstrap.RadioSet
36356  * @extends Roo.bootstrap.Input
36357  * Bootstrap RadioSet class
36358  * @cfg {String} indicatorpos (left|right) default left
36359  * @cfg {Boolean} inline (true|false) inline the element (default true)
36360  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36361  * @constructor
36362  * Create a new RadioSet
36363  * @param {Object} config The config object
36364  */
36365
36366 Roo.bootstrap.RadioSet = function(config){
36367     
36368     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36369     
36370     this.radioes = [];
36371     
36372     Roo.bootstrap.RadioSet.register(this);
36373     
36374     this.addEvents({
36375         /**
36376         * @event check
36377         * Fires when the element is checked or unchecked.
36378         * @param {Roo.bootstrap.RadioSet} this This radio
36379         * @param {Roo.bootstrap.Radio} item The checked item
36380         */
36381        check : true,
36382        /**
36383         * @event click
36384         * Fires when the element is click.
36385         * @param {Roo.bootstrap.RadioSet} this This radio set
36386         * @param {Roo.bootstrap.Radio} item The checked item
36387         * @param {Roo.EventObject} e The event object
36388         */
36389        click : true
36390     });
36391     
36392 };
36393
36394 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36395
36396     radioes : false,
36397     
36398     inline : true,
36399     
36400     weight : '',
36401     
36402     indicatorpos : 'left',
36403     
36404     getAutoCreate : function()
36405     {
36406         var label = {
36407             tag : 'label',
36408             cls : 'roo-radio-set-label',
36409             cn : [
36410                 {
36411                     tag : 'span',
36412                     html : this.fieldLabel
36413                 }
36414             ]
36415         };
36416         if (Roo.bootstrap.version == 3) {
36417             
36418             
36419             if(this.indicatorpos == 'left'){
36420                 label.cn.unshift({
36421                     tag : 'i',
36422                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36423                     tooltip : 'This field is required'
36424                 });
36425             } else {
36426                 label.cn.push({
36427                     tag : 'i',
36428                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36429                     tooltip : 'This field is required'
36430                 });
36431             }
36432         }
36433         var items = {
36434             tag : 'div',
36435             cls : 'roo-radio-set-items'
36436         };
36437         
36438         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36439         
36440         if (align === 'left' && this.fieldLabel.length) {
36441             
36442             items = {
36443                 cls : "roo-radio-set-right", 
36444                 cn: [
36445                     items
36446                 ]
36447             };
36448             
36449             if(this.labelWidth > 12){
36450                 label.style = "width: " + this.labelWidth + 'px';
36451             }
36452             
36453             if(this.labelWidth < 13 && this.labelmd == 0){
36454                 this.labelmd = this.labelWidth;
36455             }
36456             
36457             if(this.labellg > 0){
36458                 label.cls += ' col-lg-' + this.labellg;
36459                 items.cls += ' col-lg-' + (12 - this.labellg);
36460             }
36461             
36462             if(this.labelmd > 0){
36463                 label.cls += ' col-md-' + this.labelmd;
36464                 items.cls += ' col-md-' + (12 - this.labelmd);
36465             }
36466             
36467             if(this.labelsm > 0){
36468                 label.cls += ' col-sm-' + this.labelsm;
36469                 items.cls += ' col-sm-' + (12 - this.labelsm);
36470             }
36471             
36472             if(this.labelxs > 0){
36473                 label.cls += ' col-xs-' + this.labelxs;
36474                 items.cls += ' col-xs-' + (12 - this.labelxs);
36475             }
36476         }
36477         
36478         var cfg = {
36479             tag : 'div',
36480             cls : 'roo-radio-set',
36481             cn : [
36482                 {
36483                     tag : 'input',
36484                     cls : 'roo-radio-set-input',
36485                     type : 'hidden',
36486                     name : this.name,
36487                     value : this.value ? this.value :  ''
36488                 },
36489                 label,
36490                 items
36491             ]
36492         };
36493         
36494         if(this.weight.length){
36495             cfg.cls += ' roo-radio-' + this.weight;
36496         }
36497         
36498         if(this.inline) {
36499             cfg.cls += ' roo-radio-set-inline';
36500         }
36501         
36502         var settings=this;
36503         ['xs','sm','md','lg'].map(function(size){
36504             if (settings[size]) {
36505                 cfg.cls += ' col-' + size + '-' + settings[size];
36506             }
36507         });
36508         
36509         return cfg;
36510         
36511     },
36512
36513     initEvents : function()
36514     {
36515         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36516         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36517         
36518         if(!this.fieldLabel.length){
36519             this.labelEl.hide();
36520         }
36521         
36522         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36523         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36524         
36525         this.indicator = this.indicatorEl();
36526         
36527         if(this.indicator){
36528             this.indicator.addClass('invisible');
36529         }
36530         
36531         this.originalValue = this.getValue();
36532         
36533     },
36534     
36535     inputEl: function ()
36536     {
36537         return this.el.select('.roo-radio-set-input', true).first();
36538     },
36539     
36540     getChildContainer : function()
36541     {
36542         return this.itemsEl;
36543     },
36544     
36545     register : function(item)
36546     {
36547         this.radioes.push(item);
36548         
36549     },
36550     
36551     validate : function()
36552     {   
36553         if(this.getVisibilityEl().hasClass('hidden')){
36554             return true;
36555         }
36556         
36557         var valid = false;
36558         
36559         Roo.each(this.radioes, function(i){
36560             if(!i.checked){
36561                 return;
36562             }
36563             
36564             valid = true;
36565             return false;
36566         });
36567         
36568         if(this.allowBlank) {
36569             return true;
36570         }
36571         
36572         if(this.disabled || valid){
36573             this.markValid();
36574             return true;
36575         }
36576         
36577         this.markInvalid();
36578         return false;
36579         
36580     },
36581     
36582     markValid : function()
36583     {
36584         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36585             this.indicatorEl().removeClass('visible');
36586             this.indicatorEl().addClass('invisible');
36587         }
36588         
36589         
36590         if (Roo.bootstrap.version == 3) {
36591             this.el.removeClass([this.invalidClass, this.validClass]);
36592             this.el.addClass(this.validClass);
36593         } else {
36594             this.el.removeClass(['is-invalid','is-valid']);
36595             this.el.addClass(['is-valid']);
36596         }
36597         this.fireEvent('valid', this);
36598     },
36599     
36600     markInvalid : function(msg)
36601     {
36602         if(this.allowBlank || this.disabled){
36603             return;
36604         }
36605         
36606         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36607             this.indicatorEl().removeClass('invisible');
36608             this.indicatorEl().addClass('visible');
36609         }
36610         if (Roo.bootstrap.version == 3) {
36611             this.el.removeClass([this.invalidClass, this.validClass]);
36612             this.el.addClass(this.invalidClass);
36613         } else {
36614             this.el.removeClass(['is-invalid','is-valid']);
36615             this.el.addClass(['is-invalid']);
36616         }
36617         
36618         this.fireEvent('invalid', this, msg);
36619         
36620     },
36621     
36622     setValue : function(v, suppressEvent)
36623     {   
36624         if(this.value === v){
36625             return;
36626         }
36627         
36628         this.value = v;
36629         
36630         if(this.rendered){
36631             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36632         }
36633         
36634         Roo.each(this.radioes, function(i){
36635             i.checked = false;
36636             i.el.removeClass('checked');
36637         });
36638         
36639         Roo.each(this.radioes, function(i){
36640             
36641             if(i.value === v || i.value.toString() === v.toString()){
36642                 i.checked = true;
36643                 i.el.addClass('checked');
36644                 
36645                 if(suppressEvent !== true){
36646                     this.fireEvent('check', this, i);
36647                 }
36648                 
36649                 return false;
36650             }
36651             
36652         }, this);
36653         
36654         this.validate();
36655     },
36656     
36657     clearInvalid : function(){
36658         
36659         if(!this.el || this.preventMark){
36660             return;
36661         }
36662         
36663         this.el.removeClass([this.invalidClass]);
36664         
36665         this.fireEvent('valid', this);
36666     }
36667     
36668 });
36669
36670 Roo.apply(Roo.bootstrap.RadioSet, {
36671     
36672     groups: {},
36673     
36674     register : function(set)
36675     {
36676         this.groups[set.name] = set;
36677     },
36678     
36679     get: function(name) 
36680     {
36681         if (typeof(this.groups[name]) == 'undefined') {
36682             return false;
36683         }
36684         
36685         return this.groups[name] ;
36686     }
36687     
36688 });
36689 /*
36690  * Based on:
36691  * Ext JS Library 1.1.1
36692  * Copyright(c) 2006-2007, Ext JS, LLC.
36693  *
36694  * Originally Released Under LGPL - original licence link has changed is not relivant.
36695  *
36696  * Fork - LGPL
36697  * <script type="text/javascript">
36698  */
36699
36700
36701 /**
36702  * @class Roo.bootstrap.SplitBar
36703  * @extends Roo.util.Observable
36704  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36705  * <br><br>
36706  * Usage:
36707  * <pre><code>
36708 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36709                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36710 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36711 split.minSize = 100;
36712 split.maxSize = 600;
36713 split.animate = true;
36714 split.on('moved', splitterMoved);
36715 </code></pre>
36716  * @constructor
36717  * Create a new SplitBar
36718  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36719  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36720  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36721  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36722                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36723                         position of the SplitBar).
36724  */
36725 Roo.bootstrap.SplitBar = function(cfg){
36726     
36727     /** @private */
36728     
36729     //{
36730     //  dragElement : elm
36731     //  resizingElement: el,
36732         // optional..
36733     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36734     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36735         // existingProxy ???
36736     //}
36737     
36738     this.el = Roo.get(cfg.dragElement, true);
36739     this.el.dom.unselectable = "on";
36740     /** @private */
36741     this.resizingEl = Roo.get(cfg.resizingElement, true);
36742
36743     /**
36744      * @private
36745      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36746      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36747      * @type Number
36748      */
36749     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36750     
36751     /**
36752      * The minimum size of the resizing element. (Defaults to 0)
36753      * @type Number
36754      */
36755     this.minSize = 0;
36756     
36757     /**
36758      * The maximum size of the resizing element. (Defaults to 2000)
36759      * @type Number
36760      */
36761     this.maxSize = 2000;
36762     
36763     /**
36764      * Whether to animate the transition to the new size
36765      * @type Boolean
36766      */
36767     this.animate = false;
36768     
36769     /**
36770      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36771      * @type Boolean
36772      */
36773     this.useShim = false;
36774     
36775     /** @private */
36776     this.shim = null;
36777     
36778     if(!cfg.existingProxy){
36779         /** @private */
36780         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36781     }else{
36782         this.proxy = Roo.get(cfg.existingProxy).dom;
36783     }
36784     /** @private */
36785     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36786     
36787     /** @private */
36788     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36789     
36790     /** @private */
36791     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36792     
36793     /** @private */
36794     this.dragSpecs = {};
36795     
36796     /**
36797      * @private The adapter to use to positon and resize elements
36798      */
36799     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36800     this.adapter.init(this);
36801     
36802     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36803         /** @private */
36804         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36805         this.el.addClass("roo-splitbar-h");
36806     }else{
36807         /** @private */
36808         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36809         this.el.addClass("roo-splitbar-v");
36810     }
36811     
36812     this.addEvents({
36813         /**
36814          * @event resize
36815          * Fires when the splitter is moved (alias for {@link #event-moved})
36816          * @param {Roo.bootstrap.SplitBar} this
36817          * @param {Number} newSize the new width or height
36818          */
36819         "resize" : true,
36820         /**
36821          * @event moved
36822          * Fires when the splitter is moved
36823          * @param {Roo.bootstrap.SplitBar} this
36824          * @param {Number} newSize the new width or height
36825          */
36826         "moved" : true,
36827         /**
36828          * @event beforeresize
36829          * Fires before the splitter is dragged
36830          * @param {Roo.bootstrap.SplitBar} this
36831          */
36832         "beforeresize" : true,
36833
36834         "beforeapply" : true
36835     });
36836
36837     Roo.util.Observable.call(this);
36838 };
36839
36840 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36841     onStartProxyDrag : function(x, y){
36842         this.fireEvent("beforeresize", this);
36843         if(!this.overlay){
36844             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36845             o.unselectable();
36846             o.enableDisplayMode("block");
36847             // all splitbars share the same overlay
36848             Roo.bootstrap.SplitBar.prototype.overlay = o;
36849         }
36850         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36851         this.overlay.show();
36852         Roo.get(this.proxy).setDisplayed("block");
36853         var size = this.adapter.getElementSize(this);
36854         this.activeMinSize = this.getMinimumSize();;
36855         this.activeMaxSize = this.getMaximumSize();;
36856         var c1 = size - this.activeMinSize;
36857         var c2 = Math.max(this.activeMaxSize - size, 0);
36858         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36859             this.dd.resetConstraints();
36860             this.dd.setXConstraint(
36861                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36862                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36863             );
36864             this.dd.setYConstraint(0, 0);
36865         }else{
36866             this.dd.resetConstraints();
36867             this.dd.setXConstraint(0, 0);
36868             this.dd.setYConstraint(
36869                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36870                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36871             );
36872          }
36873         this.dragSpecs.startSize = size;
36874         this.dragSpecs.startPoint = [x, y];
36875         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36876     },
36877     
36878     /** 
36879      * @private Called after the drag operation by the DDProxy
36880      */
36881     onEndProxyDrag : function(e){
36882         Roo.get(this.proxy).setDisplayed(false);
36883         var endPoint = Roo.lib.Event.getXY(e);
36884         if(this.overlay){
36885             this.overlay.hide();
36886         }
36887         var newSize;
36888         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36889             newSize = this.dragSpecs.startSize + 
36890                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36891                     endPoint[0] - this.dragSpecs.startPoint[0] :
36892                     this.dragSpecs.startPoint[0] - endPoint[0]
36893                 );
36894         }else{
36895             newSize = this.dragSpecs.startSize + 
36896                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36897                     endPoint[1] - this.dragSpecs.startPoint[1] :
36898                     this.dragSpecs.startPoint[1] - endPoint[1]
36899                 );
36900         }
36901         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36902         if(newSize != this.dragSpecs.startSize){
36903             if(this.fireEvent('beforeapply', this, newSize) !== false){
36904                 this.adapter.setElementSize(this, newSize);
36905                 this.fireEvent("moved", this, newSize);
36906                 this.fireEvent("resize", this, newSize);
36907             }
36908         }
36909     },
36910     
36911     /**
36912      * Get the adapter this SplitBar uses
36913      * @return The adapter object
36914      */
36915     getAdapter : function(){
36916         return this.adapter;
36917     },
36918     
36919     /**
36920      * Set the adapter this SplitBar uses
36921      * @param {Object} adapter A SplitBar adapter object
36922      */
36923     setAdapter : function(adapter){
36924         this.adapter = adapter;
36925         this.adapter.init(this);
36926     },
36927     
36928     /**
36929      * Gets the minimum size for the resizing element
36930      * @return {Number} The minimum size
36931      */
36932     getMinimumSize : function(){
36933         return this.minSize;
36934     },
36935     
36936     /**
36937      * Sets the minimum size for the resizing element
36938      * @param {Number} minSize The minimum size
36939      */
36940     setMinimumSize : function(minSize){
36941         this.minSize = minSize;
36942     },
36943     
36944     /**
36945      * Gets the maximum size for the resizing element
36946      * @return {Number} The maximum size
36947      */
36948     getMaximumSize : function(){
36949         return this.maxSize;
36950     },
36951     
36952     /**
36953      * Sets the maximum size for the resizing element
36954      * @param {Number} maxSize The maximum size
36955      */
36956     setMaximumSize : function(maxSize){
36957         this.maxSize = maxSize;
36958     },
36959     
36960     /**
36961      * Sets the initialize size for the resizing element
36962      * @param {Number} size The initial size
36963      */
36964     setCurrentSize : function(size){
36965         var oldAnimate = this.animate;
36966         this.animate = false;
36967         this.adapter.setElementSize(this, size);
36968         this.animate = oldAnimate;
36969     },
36970     
36971     /**
36972      * Destroy this splitbar. 
36973      * @param {Boolean} removeEl True to remove the element
36974      */
36975     destroy : function(removeEl){
36976         if(this.shim){
36977             this.shim.remove();
36978         }
36979         this.dd.unreg();
36980         this.proxy.parentNode.removeChild(this.proxy);
36981         if(removeEl){
36982             this.el.remove();
36983         }
36984     }
36985 });
36986
36987 /**
36988  * @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.
36989  */
36990 Roo.bootstrap.SplitBar.createProxy = function(dir){
36991     var proxy = new Roo.Element(document.createElement("div"));
36992     proxy.unselectable();
36993     var cls = 'roo-splitbar-proxy';
36994     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36995     document.body.appendChild(proxy.dom);
36996     return proxy.dom;
36997 };
36998
36999 /** 
37000  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37001  * Default Adapter. It assumes the splitter and resizing element are not positioned
37002  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37003  */
37004 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37005 };
37006
37007 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37008     // do nothing for now
37009     init : function(s){
37010     
37011     },
37012     /**
37013      * Called before drag operations to get the current size of the resizing element. 
37014      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37015      */
37016      getElementSize : function(s){
37017         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37018             return s.resizingEl.getWidth();
37019         }else{
37020             return s.resizingEl.getHeight();
37021         }
37022     },
37023     
37024     /**
37025      * Called after drag operations to set the size of the resizing element.
37026      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37027      * @param {Number} newSize The new size to set
37028      * @param {Function} onComplete A function to be invoked when resizing is complete
37029      */
37030     setElementSize : function(s, newSize, onComplete){
37031         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37032             if(!s.animate){
37033                 s.resizingEl.setWidth(newSize);
37034                 if(onComplete){
37035                     onComplete(s, newSize);
37036                 }
37037             }else{
37038                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37039             }
37040         }else{
37041             
37042             if(!s.animate){
37043                 s.resizingEl.setHeight(newSize);
37044                 if(onComplete){
37045                     onComplete(s, newSize);
37046                 }
37047             }else{
37048                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37049             }
37050         }
37051     }
37052 };
37053
37054 /** 
37055  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37056  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37057  * Adapter that  moves the splitter element to align with the resized sizing element. 
37058  * Used with an absolute positioned SplitBar.
37059  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37060  * document.body, make sure you assign an id to the body element.
37061  */
37062 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37063     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37064     this.container = Roo.get(container);
37065 };
37066
37067 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37068     init : function(s){
37069         this.basic.init(s);
37070     },
37071     
37072     getElementSize : function(s){
37073         return this.basic.getElementSize(s);
37074     },
37075     
37076     setElementSize : function(s, newSize, onComplete){
37077         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37078     },
37079     
37080     moveSplitter : function(s){
37081         var yes = Roo.bootstrap.SplitBar;
37082         switch(s.placement){
37083             case yes.LEFT:
37084                 s.el.setX(s.resizingEl.getRight());
37085                 break;
37086             case yes.RIGHT:
37087                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37088                 break;
37089             case yes.TOP:
37090                 s.el.setY(s.resizingEl.getBottom());
37091                 break;
37092             case yes.BOTTOM:
37093                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37094                 break;
37095         }
37096     }
37097 };
37098
37099 /**
37100  * Orientation constant - Create a vertical SplitBar
37101  * @static
37102  * @type Number
37103  */
37104 Roo.bootstrap.SplitBar.VERTICAL = 1;
37105
37106 /**
37107  * Orientation constant - Create a horizontal SplitBar
37108  * @static
37109  * @type Number
37110  */
37111 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37112
37113 /**
37114  * Placement constant - The resizing element is to the left of the splitter element
37115  * @static
37116  * @type Number
37117  */
37118 Roo.bootstrap.SplitBar.LEFT = 1;
37119
37120 /**
37121  * Placement constant - The resizing element is to the right of the splitter element
37122  * @static
37123  * @type Number
37124  */
37125 Roo.bootstrap.SplitBar.RIGHT = 2;
37126
37127 /**
37128  * Placement constant - The resizing element is positioned above the splitter element
37129  * @static
37130  * @type Number
37131  */
37132 Roo.bootstrap.SplitBar.TOP = 3;
37133
37134 /**
37135  * Placement constant - The resizing element is positioned under splitter element
37136  * @static
37137  * @type Number
37138  */
37139 Roo.bootstrap.SplitBar.BOTTOM = 4;
37140 Roo.namespace("Roo.bootstrap.layout");/*
37141  * Based on:
37142  * Ext JS Library 1.1.1
37143  * Copyright(c) 2006-2007, Ext JS, LLC.
37144  *
37145  * Originally Released Under LGPL - original licence link has changed is not relivant.
37146  *
37147  * Fork - LGPL
37148  * <script type="text/javascript">
37149  */
37150
37151 /**
37152  * @class Roo.bootstrap.layout.Manager
37153  * @extends Roo.bootstrap.Component
37154  * Base class for layout managers.
37155  */
37156 Roo.bootstrap.layout.Manager = function(config)
37157 {
37158     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37159
37160
37161
37162
37163
37164     /** false to disable window resize monitoring @type Boolean */
37165     this.monitorWindowResize = true;
37166     this.regions = {};
37167     this.addEvents({
37168         /**
37169          * @event layout
37170          * Fires when a layout is performed.
37171          * @param {Roo.LayoutManager} this
37172          */
37173         "layout" : true,
37174         /**
37175          * @event regionresized
37176          * Fires when the user resizes a region.
37177          * @param {Roo.LayoutRegion} region The resized region
37178          * @param {Number} newSize The new size (width for east/west, height for north/south)
37179          */
37180         "regionresized" : true,
37181         /**
37182          * @event regioncollapsed
37183          * Fires when a region is collapsed.
37184          * @param {Roo.LayoutRegion} region The collapsed region
37185          */
37186         "regioncollapsed" : true,
37187         /**
37188          * @event regionexpanded
37189          * Fires when a region is expanded.
37190          * @param {Roo.LayoutRegion} region The expanded region
37191          */
37192         "regionexpanded" : true
37193     });
37194     this.updating = false;
37195
37196     if (config.el) {
37197         this.el = Roo.get(config.el);
37198         this.initEvents();
37199     }
37200
37201 };
37202
37203 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37204
37205
37206     regions : null,
37207
37208     monitorWindowResize : true,
37209
37210
37211     updating : false,
37212
37213
37214     onRender : function(ct, position)
37215     {
37216         if(!this.el){
37217             this.el = Roo.get(ct);
37218             this.initEvents();
37219         }
37220         //this.fireEvent('render',this);
37221     },
37222
37223
37224     initEvents: function()
37225     {
37226
37227
37228         // ie scrollbar fix
37229         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37230             document.body.scroll = "no";
37231         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37232             this.el.position('relative');
37233         }
37234         this.id = this.el.id;
37235         this.el.addClass("roo-layout-container");
37236         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37237         if(this.el.dom != document.body ) {
37238             this.el.on('resize', this.layout,this);
37239             this.el.on('show', this.layout,this);
37240         }
37241
37242     },
37243
37244     /**
37245      * Returns true if this layout is currently being updated
37246      * @return {Boolean}
37247      */
37248     isUpdating : function(){
37249         return this.updating;
37250     },
37251
37252     /**
37253      * Suspend the LayoutManager from doing auto-layouts while
37254      * making multiple add or remove calls
37255      */
37256     beginUpdate : function(){
37257         this.updating = true;
37258     },
37259
37260     /**
37261      * Restore auto-layouts and optionally disable the manager from performing a layout
37262      * @param {Boolean} noLayout true to disable a layout update
37263      */
37264     endUpdate : function(noLayout){
37265         this.updating = false;
37266         if(!noLayout){
37267             this.layout();
37268         }
37269     },
37270
37271     layout: function(){
37272         // abstract...
37273     },
37274
37275     onRegionResized : function(region, newSize){
37276         this.fireEvent("regionresized", region, newSize);
37277         this.layout();
37278     },
37279
37280     onRegionCollapsed : function(region){
37281         this.fireEvent("regioncollapsed", region);
37282     },
37283
37284     onRegionExpanded : function(region){
37285         this.fireEvent("regionexpanded", region);
37286     },
37287
37288     /**
37289      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37290      * performs box-model adjustments.
37291      * @return {Object} The size as an object {width: (the width), height: (the height)}
37292      */
37293     getViewSize : function()
37294     {
37295         var size;
37296         if(this.el.dom != document.body){
37297             size = this.el.getSize();
37298         }else{
37299             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37300         }
37301         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37302         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37303         return size;
37304     },
37305
37306     /**
37307      * Returns the Element this layout is bound to.
37308      * @return {Roo.Element}
37309      */
37310     getEl : function(){
37311         return this.el;
37312     },
37313
37314     /**
37315      * Returns the specified region.
37316      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37317      * @return {Roo.LayoutRegion}
37318      */
37319     getRegion : function(target){
37320         return this.regions[target.toLowerCase()];
37321     },
37322
37323     onWindowResize : function(){
37324         if(this.monitorWindowResize){
37325             this.layout();
37326         }
37327     }
37328 });
37329 /*
37330  * Based on:
37331  * Ext JS Library 1.1.1
37332  * Copyright(c) 2006-2007, Ext JS, LLC.
37333  *
37334  * Originally Released Under LGPL - original licence link has changed is not relivant.
37335  *
37336  * Fork - LGPL
37337  * <script type="text/javascript">
37338  */
37339 /**
37340  * @class Roo.bootstrap.layout.Border
37341  * @extends Roo.bootstrap.layout.Manager
37342  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37343  * please see: examples/bootstrap/nested.html<br><br>
37344  
37345 <b>The container the layout is rendered into can be either the body element or any other element.
37346 If it is not the body element, the container needs to either be an absolute positioned element,
37347 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37348 the container size if it is not the body element.</b>
37349
37350 * @constructor
37351 * Create a new Border
37352 * @param {Object} config Configuration options
37353  */
37354 Roo.bootstrap.layout.Border = function(config){
37355     config = config || {};
37356     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37357     
37358     
37359     
37360     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37361         if(config[region]){
37362             config[region].region = region;
37363             this.addRegion(config[region]);
37364         }
37365     },this);
37366     
37367 };
37368
37369 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37370
37371 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37372     
37373     parent : false, // this might point to a 'nest' or a ???
37374     
37375     /**
37376      * Creates and adds a new region if it doesn't already exist.
37377      * @param {String} target The target region key (north, south, east, west or center).
37378      * @param {Object} config The regions config object
37379      * @return {BorderLayoutRegion} The new region
37380      */
37381     addRegion : function(config)
37382     {
37383         if(!this.regions[config.region]){
37384             var r = this.factory(config);
37385             this.bindRegion(r);
37386         }
37387         return this.regions[config.region];
37388     },
37389
37390     // private (kinda)
37391     bindRegion : function(r){
37392         this.regions[r.config.region] = r;
37393         
37394         r.on("visibilitychange",    this.layout, this);
37395         r.on("paneladded",          this.layout, this);
37396         r.on("panelremoved",        this.layout, this);
37397         r.on("invalidated",         this.layout, this);
37398         r.on("resized",             this.onRegionResized, this);
37399         r.on("collapsed",           this.onRegionCollapsed, this);
37400         r.on("expanded",            this.onRegionExpanded, this);
37401     },
37402
37403     /**
37404      * Performs a layout update.
37405      */
37406     layout : function()
37407     {
37408         if(this.updating) {
37409             return;
37410         }
37411         
37412         // render all the rebions if they have not been done alreayd?
37413         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37414             if(this.regions[region] && !this.regions[region].bodyEl){
37415                 this.regions[region].onRender(this.el)
37416             }
37417         },this);
37418         
37419         var size = this.getViewSize();
37420         var w = size.width;
37421         var h = size.height;
37422         var centerW = w;
37423         var centerH = h;
37424         var centerY = 0;
37425         var centerX = 0;
37426         //var x = 0, y = 0;
37427
37428         var rs = this.regions;
37429         var north = rs["north"];
37430         var south = rs["south"]; 
37431         var west = rs["west"];
37432         var east = rs["east"];
37433         var center = rs["center"];
37434         //if(this.hideOnLayout){ // not supported anymore
37435             //c.el.setStyle("display", "none");
37436         //}
37437         if(north && north.isVisible()){
37438             var b = north.getBox();
37439             var m = north.getMargins();
37440             b.width = w - (m.left+m.right);
37441             b.x = m.left;
37442             b.y = m.top;
37443             centerY = b.height + b.y + m.bottom;
37444             centerH -= centerY;
37445             north.updateBox(this.safeBox(b));
37446         }
37447         if(south && south.isVisible()){
37448             var b = south.getBox();
37449             var m = south.getMargins();
37450             b.width = w - (m.left+m.right);
37451             b.x = m.left;
37452             var totalHeight = (b.height + m.top + m.bottom);
37453             b.y = h - totalHeight + m.top;
37454             centerH -= totalHeight;
37455             south.updateBox(this.safeBox(b));
37456         }
37457         if(west && west.isVisible()){
37458             var b = west.getBox();
37459             var m = west.getMargins();
37460             b.height = centerH - (m.top+m.bottom);
37461             b.x = m.left;
37462             b.y = centerY + m.top;
37463             var totalWidth = (b.width + m.left + m.right);
37464             centerX += totalWidth;
37465             centerW -= totalWidth;
37466             west.updateBox(this.safeBox(b));
37467         }
37468         if(east && east.isVisible()){
37469             var b = east.getBox();
37470             var m = east.getMargins();
37471             b.height = centerH - (m.top+m.bottom);
37472             var totalWidth = (b.width + m.left + m.right);
37473             b.x = w - totalWidth + m.left;
37474             b.y = centerY + m.top;
37475             centerW -= totalWidth;
37476             east.updateBox(this.safeBox(b));
37477         }
37478         if(center){
37479             var m = center.getMargins();
37480             var centerBox = {
37481                 x: centerX + m.left,
37482                 y: centerY + m.top,
37483                 width: centerW - (m.left+m.right),
37484                 height: centerH - (m.top+m.bottom)
37485             };
37486             //if(this.hideOnLayout){
37487                 //center.el.setStyle("display", "block");
37488             //}
37489             center.updateBox(this.safeBox(centerBox));
37490         }
37491         this.el.repaint();
37492         this.fireEvent("layout", this);
37493     },
37494
37495     // private
37496     safeBox : function(box){
37497         box.width = Math.max(0, box.width);
37498         box.height = Math.max(0, box.height);
37499         return box;
37500     },
37501
37502     /**
37503      * Adds a ContentPanel (or subclass) to this layout.
37504      * @param {String} target The target region key (north, south, east, west or center).
37505      * @param {Roo.ContentPanel} panel The panel to add
37506      * @return {Roo.ContentPanel} The added panel
37507      */
37508     add : function(target, panel){
37509          
37510         target = target.toLowerCase();
37511         return this.regions[target].add(panel);
37512     },
37513
37514     /**
37515      * Remove a ContentPanel (or subclass) to this layout.
37516      * @param {String} target The target region key (north, south, east, west or center).
37517      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37518      * @return {Roo.ContentPanel} The removed panel
37519      */
37520     remove : function(target, panel){
37521         target = target.toLowerCase();
37522         return this.regions[target].remove(panel);
37523     },
37524
37525     /**
37526      * Searches all regions for a panel with the specified id
37527      * @param {String} panelId
37528      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37529      */
37530     findPanel : function(panelId){
37531         var rs = this.regions;
37532         for(var target in rs){
37533             if(typeof rs[target] != "function"){
37534                 var p = rs[target].getPanel(panelId);
37535                 if(p){
37536                     return p;
37537                 }
37538             }
37539         }
37540         return null;
37541     },
37542
37543     /**
37544      * Searches all regions for a panel with the specified id and activates (shows) it.
37545      * @param {String/ContentPanel} panelId The panels id or the panel itself
37546      * @return {Roo.ContentPanel} The shown panel or null
37547      */
37548     showPanel : function(panelId) {
37549       var rs = this.regions;
37550       for(var target in rs){
37551          var r = rs[target];
37552          if(typeof r != "function"){
37553             if(r.hasPanel(panelId)){
37554                return r.showPanel(panelId);
37555             }
37556          }
37557       }
37558       return null;
37559    },
37560
37561    /**
37562      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37563      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37564      */
37565    /*
37566     restoreState : function(provider){
37567         if(!provider){
37568             provider = Roo.state.Manager;
37569         }
37570         var sm = new Roo.LayoutStateManager();
37571         sm.init(this, provider);
37572     },
37573 */
37574  
37575  
37576     /**
37577      * Adds a xtype elements to the layout.
37578      * <pre><code>
37579
37580 layout.addxtype({
37581        xtype : 'ContentPanel',
37582        region: 'west',
37583        items: [ .... ]
37584    }
37585 );
37586
37587 layout.addxtype({
37588         xtype : 'NestedLayoutPanel',
37589         region: 'west',
37590         layout: {
37591            center: { },
37592            west: { }   
37593         },
37594         items : [ ... list of content panels or nested layout panels.. ]
37595    }
37596 );
37597 </code></pre>
37598      * @param {Object} cfg Xtype definition of item to add.
37599      */
37600     addxtype : function(cfg)
37601     {
37602         // basically accepts a pannel...
37603         // can accept a layout region..!?!?
37604         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37605         
37606         
37607         // theory?  children can only be panels??
37608         
37609         //if (!cfg.xtype.match(/Panel$/)) {
37610         //    return false;
37611         //}
37612         var ret = false;
37613         
37614         if (typeof(cfg.region) == 'undefined') {
37615             Roo.log("Failed to add Panel, region was not set");
37616             Roo.log(cfg);
37617             return false;
37618         }
37619         var region = cfg.region;
37620         delete cfg.region;
37621         
37622           
37623         var xitems = [];
37624         if (cfg.items) {
37625             xitems = cfg.items;
37626             delete cfg.items;
37627         }
37628         var nb = false;
37629         
37630         if ( region == 'center') {
37631             Roo.log("Center: " + cfg.title);
37632         }
37633         
37634         
37635         switch(cfg.xtype) 
37636         {
37637             case 'Content':  // ContentPanel (el, cfg)
37638             case 'Scroll':  // ContentPanel (el, cfg)
37639             case 'View': 
37640                 cfg.autoCreate = cfg.autoCreate || true;
37641                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37642                 //} else {
37643                 //    var el = this.el.createChild();
37644                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37645                 //}
37646                 
37647                 this.add(region, ret);
37648                 break;
37649             
37650             /*
37651             case 'TreePanel': // our new panel!
37652                 cfg.el = this.el.createChild();
37653                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37654                 this.add(region, ret);
37655                 break;
37656             */
37657             
37658             case 'Nest': 
37659                 // create a new Layout (which is  a Border Layout...
37660                 
37661                 var clayout = cfg.layout;
37662                 clayout.el  = this.el.createChild();
37663                 clayout.items   = clayout.items  || [];
37664                 
37665                 delete cfg.layout;
37666                 
37667                 // replace this exitems with the clayout ones..
37668                 xitems = clayout.items;
37669                  
37670                 // force background off if it's in center...
37671                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37672                     cfg.background = false;
37673                 }
37674                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37675                 
37676                 
37677                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37678                 //console.log('adding nested layout panel '  + cfg.toSource());
37679                 this.add(region, ret);
37680                 nb = {}; /// find first...
37681                 break;
37682             
37683             case 'Grid':
37684                 
37685                 // needs grid and region
37686                 
37687                 //var el = this.getRegion(region).el.createChild();
37688                 /*
37689                  *var el = this.el.createChild();
37690                 // create the grid first...
37691                 cfg.grid.container = el;
37692                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37693                 */
37694                 
37695                 if (region == 'center' && this.active ) {
37696                     cfg.background = false;
37697                 }
37698                 
37699                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37700                 
37701                 this.add(region, ret);
37702                 /*
37703                 if (cfg.background) {
37704                     // render grid on panel activation (if panel background)
37705                     ret.on('activate', function(gp) {
37706                         if (!gp.grid.rendered) {
37707                     //        gp.grid.render(el);
37708                         }
37709                     });
37710                 } else {
37711                   //  cfg.grid.render(el);
37712                 }
37713                 */
37714                 break;
37715            
37716            
37717             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37718                 // it was the old xcomponent building that caused this before.
37719                 // espeically if border is the top element in the tree.
37720                 ret = this;
37721                 break; 
37722                 
37723                     
37724                 
37725                 
37726                 
37727             default:
37728                 /*
37729                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37730                     
37731                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37732                     this.add(region, ret);
37733                 } else {
37734                 */
37735                     Roo.log(cfg);
37736                     throw "Can not add '" + cfg.xtype + "' to Border";
37737                     return null;
37738              
37739                                 
37740              
37741         }
37742         this.beginUpdate();
37743         // add children..
37744         var region = '';
37745         var abn = {};
37746         Roo.each(xitems, function(i)  {
37747             region = nb && i.region ? i.region : false;
37748             
37749             var add = ret.addxtype(i);
37750            
37751             if (region) {
37752                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37753                 if (!i.background) {
37754                     abn[region] = nb[region] ;
37755                 }
37756             }
37757             
37758         });
37759         this.endUpdate();
37760
37761         // make the last non-background panel active..
37762         //if (nb) { Roo.log(abn); }
37763         if (nb) {
37764             
37765             for(var r in abn) {
37766                 region = this.getRegion(r);
37767                 if (region) {
37768                     // tried using nb[r], but it does not work..
37769                      
37770                     region.showPanel(abn[r]);
37771                    
37772                 }
37773             }
37774         }
37775         return ret;
37776         
37777     },
37778     
37779     
37780 // private
37781     factory : function(cfg)
37782     {
37783         
37784         var validRegions = Roo.bootstrap.layout.Border.regions;
37785
37786         var target = cfg.region;
37787         cfg.mgr = this;
37788         
37789         var r = Roo.bootstrap.layout;
37790         Roo.log(target);
37791         switch(target){
37792             case "north":
37793                 return new r.North(cfg);
37794             case "south":
37795                 return new r.South(cfg);
37796             case "east":
37797                 return new r.East(cfg);
37798             case "west":
37799                 return new r.West(cfg);
37800             case "center":
37801                 return new r.Center(cfg);
37802         }
37803         throw 'Layout region "'+target+'" not supported.';
37804     }
37805     
37806     
37807 });
37808  /*
37809  * Based on:
37810  * Ext JS Library 1.1.1
37811  * Copyright(c) 2006-2007, Ext JS, LLC.
37812  *
37813  * Originally Released Under LGPL - original licence link has changed is not relivant.
37814  *
37815  * Fork - LGPL
37816  * <script type="text/javascript">
37817  */
37818  
37819 /**
37820  * @class Roo.bootstrap.layout.Basic
37821  * @extends Roo.util.Observable
37822  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37823  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37824  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37825  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37826  * @cfg {string}   region  the region that it inhabits..
37827  * @cfg {bool}   skipConfig skip config?
37828  * 
37829
37830  */
37831 Roo.bootstrap.layout.Basic = function(config){
37832     
37833     this.mgr = config.mgr;
37834     
37835     this.position = config.region;
37836     
37837     var skipConfig = config.skipConfig;
37838     
37839     this.events = {
37840         /**
37841          * @scope Roo.BasicLayoutRegion
37842          */
37843         
37844         /**
37845          * @event beforeremove
37846          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37847          * @param {Roo.LayoutRegion} this
37848          * @param {Roo.ContentPanel} panel The panel
37849          * @param {Object} e The cancel event object
37850          */
37851         "beforeremove" : true,
37852         /**
37853          * @event invalidated
37854          * Fires when the layout for this region is changed.
37855          * @param {Roo.LayoutRegion} this
37856          */
37857         "invalidated" : true,
37858         /**
37859          * @event visibilitychange
37860          * Fires when this region is shown or hidden 
37861          * @param {Roo.LayoutRegion} this
37862          * @param {Boolean} visibility true or false
37863          */
37864         "visibilitychange" : true,
37865         /**
37866          * @event paneladded
37867          * Fires when a panel is added. 
37868          * @param {Roo.LayoutRegion} this
37869          * @param {Roo.ContentPanel} panel The panel
37870          */
37871         "paneladded" : true,
37872         /**
37873          * @event panelremoved
37874          * Fires when a panel is removed. 
37875          * @param {Roo.LayoutRegion} this
37876          * @param {Roo.ContentPanel} panel The panel
37877          */
37878         "panelremoved" : true,
37879         /**
37880          * @event beforecollapse
37881          * Fires when this region before collapse.
37882          * @param {Roo.LayoutRegion} this
37883          */
37884         "beforecollapse" : true,
37885         /**
37886          * @event collapsed
37887          * Fires when this region is collapsed.
37888          * @param {Roo.LayoutRegion} this
37889          */
37890         "collapsed" : true,
37891         /**
37892          * @event expanded
37893          * Fires when this region is expanded.
37894          * @param {Roo.LayoutRegion} this
37895          */
37896         "expanded" : true,
37897         /**
37898          * @event slideshow
37899          * Fires when this region is slid into view.
37900          * @param {Roo.LayoutRegion} this
37901          */
37902         "slideshow" : true,
37903         /**
37904          * @event slidehide
37905          * Fires when this region slides out of view. 
37906          * @param {Roo.LayoutRegion} this
37907          */
37908         "slidehide" : true,
37909         /**
37910          * @event panelactivated
37911          * Fires when a panel is activated. 
37912          * @param {Roo.LayoutRegion} this
37913          * @param {Roo.ContentPanel} panel The activated panel
37914          */
37915         "panelactivated" : true,
37916         /**
37917          * @event resized
37918          * Fires when the user resizes this region. 
37919          * @param {Roo.LayoutRegion} this
37920          * @param {Number} newSize The new size (width for east/west, height for north/south)
37921          */
37922         "resized" : true
37923     };
37924     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37925     this.panels = new Roo.util.MixedCollection();
37926     this.panels.getKey = this.getPanelId.createDelegate(this);
37927     this.box = null;
37928     this.activePanel = null;
37929     // ensure listeners are added...
37930     
37931     if (config.listeners || config.events) {
37932         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37933             listeners : config.listeners || {},
37934             events : config.events || {}
37935         });
37936     }
37937     
37938     if(skipConfig !== true){
37939         this.applyConfig(config);
37940     }
37941 };
37942
37943 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37944 {
37945     getPanelId : function(p){
37946         return p.getId();
37947     },
37948     
37949     applyConfig : function(config){
37950         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37951         this.config = config;
37952         
37953     },
37954     
37955     /**
37956      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37957      * the width, for horizontal (north, south) the height.
37958      * @param {Number} newSize The new width or height
37959      */
37960     resizeTo : function(newSize){
37961         var el = this.el ? this.el :
37962                  (this.activePanel ? this.activePanel.getEl() : null);
37963         if(el){
37964             switch(this.position){
37965                 case "east":
37966                 case "west":
37967                     el.setWidth(newSize);
37968                     this.fireEvent("resized", this, newSize);
37969                 break;
37970                 case "north":
37971                 case "south":
37972                     el.setHeight(newSize);
37973                     this.fireEvent("resized", this, newSize);
37974                 break;                
37975             }
37976         }
37977     },
37978     
37979     getBox : function(){
37980         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37981     },
37982     
37983     getMargins : function(){
37984         return this.margins;
37985     },
37986     
37987     updateBox : function(box){
37988         this.box = box;
37989         var el = this.activePanel.getEl();
37990         el.dom.style.left = box.x + "px";
37991         el.dom.style.top = box.y + "px";
37992         this.activePanel.setSize(box.width, box.height);
37993     },
37994     
37995     /**
37996      * Returns the container element for this region.
37997      * @return {Roo.Element}
37998      */
37999     getEl : function(){
38000         return this.activePanel;
38001     },
38002     
38003     /**
38004      * Returns true if this region is currently visible.
38005      * @return {Boolean}
38006      */
38007     isVisible : function(){
38008         return this.activePanel ? true : false;
38009     },
38010     
38011     setActivePanel : function(panel){
38012         panel = this.getPanel(panel);
38013         if(this.activePanel && this.activePanel != panel){
38014             this.activePanel.setActiveState(false);
38015             this.activePanel.getEl().setLeftTop(-10000,-10000);
38016         }
38017         this.activePanel = panel;
38018         panel.setActiveState(true);
38019         if(this.box){
38020             panel.setSize(this.box.width, this.box.height);
38021         }
38022         this.fireEvent("panelactivated", this, panel);
38023         this.fireEvent("invalidated");
38024     },
38025     
38026     /**
38027      * Show the specified panel.
38028      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38029      * @return {Roo.ContentPanel} The shown panel or null
38030      */
38031     showPanel : function(panel){
38032         panel = this.getPanel(panel);
38033         if(panel){
38034             this.setActivePanel(panel);
38035         }
38036         return panel;
38037     },
38038     
38039     /**
38040      * Get the active panel for this region.
38041      * @return {Roo.ContentPanel} The active panel or null
38042      */
38043     getActivePanel : function(){
38044         return this.activePanel;
38045     },
38046     
38047     /**
38048      * Add the passed ContentPanel(s)
38049      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38050      * @return {Roo.ContentPanel} The panel added (if only one was added)
38051      */
38052     add : function(panel){
38053         if(arguments.length > 1){
38054             for(var i = 0, len = arguments.length; i < len; i++) {
38055                 this.add(arguments[i]);
38056             }
38057             return null;
38058         }
38059         if(this.hasPanel(panel)){
38060             this.showPanel(panel);
38061             return panel;
38062         }
38063         var el = panel.getEl();
38064         if(el.dom.parentNode != this.mgr.el.dom){
38065             this.mgr.el.dom.appendChild(el.dom);
38066         }
38067         if(panel.setRegion){
38068             panel.setRegion(this);
38069         }
38070         this.panels.add(panel);
38071         el.setStyle("position", "absolute");
38072         if(!panel.background){
38073             this.setActivePanel(panel);
38074             if(this.config.initialSize && this.panels.getCount()==1){
38075                 this.resizeTo(this.config.initialSize);
38076             }
38077         }
38078         this.fireEvent("paneladded", this, panel);
38079         return panel;
38080     },
38081     
38082     /**
38083      * Returns true if the panel is in this region.
38084      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38085      * @return {Boolean}
38086      */
38087     hasPanel : function(panel){
38088         if(typeof panel == "object"){ // must be panel obj
38089             panel = panel.getId();
38090         }
38091         return this.getPanel(panel) ? true : false;
38092     },
38093     
38094     /**
38095      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38096      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38097      * @param {Boolean} preservePanel Overrides the config preservePanel option
38098      * @return {Roo.ContentPanel} The panel that was removed
38099      */
38100     remove : function(panel, preservePanel){
38101         panel = this.getPanel(panel);
38102         if(!panel){
38103             return null;
38104         }
38105         var e = {};
38106         this.fireEvent("beforeremove", this, panel, e);
38107         if(e.cancel === true){
38108             return null;
38109         }
38110         var panelId = panel.getId();
38111         this.panels.removeKey(panelId);
38112         return panel;
38113     },
38114     
38115     /**
38116      * Returns the panel specified or null if it's not in this region.
38117      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38118      * @return {Roo.ContentPanel}
38119      */
38120     getPanel : function(id){
38121         if(typeof id == "object"){ // must be panel obj
38122             return id;
38123         }
38124         return this.panels.get(id);
38125     },
38126     
38127     /**
38128      * Returns this regions position (north/south/east/west/center).
38129      * @return {String} 
38130      */
38131     getPosition: function(){
38132         return this.position;    
38133     }
38134 });/*
38135  * Based on:
38136  * Ext JS Library 1.1.1
38137  * Copyright(c) 2006-2007, Ext JS, LLC.
38138  *
38139  * Originally Released Under LGPL - original licence link has changed is not relivant.
38140  *
38141  * Fork - LGPL
38142  * <script type="text/javascript">
38143  */
38144  
38145 /**
38146  * @class Roo.bootstrap.layout.Region
38147  * @extends Roo.bootstrap.layout.Basic
38148  * This class represents a region in a layout manager.
38149  
38150  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38151  * @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})
38152  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38153  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38154  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38155  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38156  * @cfg {String}    title           The title for the region (overrides panel titles)
38157  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38158  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38159  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38160  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38161  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38162  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38163  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38164  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38165  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38166  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38167
38168  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38169  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38170  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38171  * @cfg {Number}    width           For East/West panels
38172  * @cfg {Number}    height          For North/South panels
38173  * @cfg {Boolean}   split           To show the splitter
38174  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38175  * 
38176  * @cfg {string}   cls             Extra CSS classes to add to region
38177  * 
38178  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38179  * @cfg {string}   region  the region that it inhabits..
38180  *
38181
38182  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38183  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38184
38185  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38186  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38187  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38188  */
38189 Roo.bootstrap.layout.Region = function(config)
38190 {
38191     this.applyConfig(config);
38192
38193     var mgr = config.mgr;
38194     var pos = config.region;
38195     config.skipConfig = true;
38196     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38197     
38198     if (mgr.el) {
38199         this.onRender(mgr.el);   
38200     }
38201      
38202     this.visible = true;
38203     this.collapsed = false;
38204     this.unrendered_panels = [];
38205 };
38206
38207 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38208
38209     position: '', // set by wrapper (eg. north/south etc..)
38210     unrendered_panels : null,  // unrendered panels.
38211     
38212     tabPosition : false,
38213     
38214     mgr: false, // points to 'Border'
38215     
38216     
38217     createBody : function(){
38218         /** This region's body element 
38219         * @type Roo.Element */
38220         this.bodyEl = this.el.createChild({
38221                 tag: "div",
38222                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38223         });
38224     },
38225
38226     onRender: function(ctr, pos)
38227     {
38228         var dh = Roo.DomHelper;
38229         /** This region's container element 
38230         * @type Roo.Element */
38231         this.el = dh.append(ctr.dom, {
38232                 tag: "div",
38233                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38234             }, true);
38235         /** This region's title element 
38236         * @type Roo.Element */
38237     
38238         this.titleEl = dh.append(this.el.dom,  {
38239                 tag: "div",
38240                 unselectable: "on",
38241                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38242                 children:[
38243                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38244                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38245                 ]
38246             }, true);
38247         
38248         this.titleEl.enableDisplayMode();
38249         /** This region's title text element 
38250         * @type HTMLElement */
38251         this.titleTextEl = this.titleEl.dom.firstChild;
38252         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38253         /*
38254         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38255         this.closeBtn.enableDisplayMode();
38256         this.closeBtn.on("click", this.closeClicked, this);
38257         this.closeBtn.hide();
38258     */
38259         this.createBody(this.config);
38260         if(this.config.hideWhenEmpty){
38261             this.hide();
38262             this.on("paneladded", this.validateVisibility, this);
38263             this.on("panelremoved", this.validateVisibility, this);
38264         }
38265         if(this.autoScroll){
38266             this.bodyEl.setStyle("overflow", "auto");
38267         }else{
38268             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38269         }
38270         //if(c.titlebar !== false){
38271             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38272                 this.titleEl.hide();
38273             }else{
38274                 this.titleEl.show();
38275                 if(this.config.title){
38276                     this.titleTextEl.innerHTML = this.config.title;
38277                 }
38278             }
38279         //}
38280         if(this.config.collapsed){
38281             this.collapse(true);
38282         }
38283         if(this.config.hidden){
38284             this.hide();
38285         }
38286         
38287         if (this.unrendered_panels && this.unrendered_panels.length) {
38288             for (var i =0;i< this.unrendered_panels.length; i++) {
38289                 this.add(this.unrendered_panels[i]);
38290             }
38291             this.unrendered_panels = null;
38292             
38293         }
38294         
38295     },
38296     
38297     applyConfig : function(c)
38298     {
38299         /*
38300          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38301             var dh = Roo.DomHelper;
38302             if(c.titlebar !== false){
38303                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38304                 this.collapseBtn.on("click", this.collapse, this);
38305                 this.collapseBtn.enableDisplayMode();
38306                 /*
38307                 if(c.showPin === true || this.showPin){
38308                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38309                     this.stickBtn.enableDisplayMode();
38310                     this.stickBtn.on("click", this.expand, this);
38311                     this.stickBtn.hide();
38312                 }
38313                 
38314             }
38315             */
38316             /** This region's collapsed element
38317             * @type Roo.Element */
38318             /*
38319              *
38320             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38321                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38322             ]}, true);
38323             
38324             if(c.floatable !== false){
38325                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38326                this.collapsedEl.on("click", this.collapseClick, this);
38327             }
38328
38329             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38330                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38331                    id: "message", unselectable: "on", style:{"float":"left"}});
38332                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38333              }
38334             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38335             this.expandBtn.on("click", this.expand, this);
38336             
38337         }
38338         
38339         if(this.collapseBtn){
38340             this.collapseBtn.setVisible(c.collapsible == true);
38341         }
38342         
38343         this.cmargins = c.cmargins || this.cmargins ||
38344                          (this.position == "west" || this.position == "east" ?
38345                              {top: 0, left: 2, right:2, bottom: 0} :
38346                              {top: 2, left: 0, right:0, bottom: 2});
38347         */
38348         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38349         
38350         
38351         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38352         
38353         this.autoScroll = c.autoScroll || false;
38354         
38355         
38356        
38357         
38358         this.duration = c.duration || .30;
38359         this.slideDuration = c.slideDuration || .45;
38360         this.config = c;
38361        
38362     },
38363     /**
38364      * Returns true if this region is currently visible.
38365      * @return {Boolean}
38366      */
38367     isVisible : function(){
38368         return this.visible;
38369     },
38370
38371     /**
38372      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38373      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38374      */
38375     //setCollapsedTitle : function(title){
38376     //    title = title || "&#160;";
38377      //   if(this.collapsedTitleTextEl){
38378       //      this.collapsedTitleTextEl.innerHTML = title;
38379        // }
38380     //},
38381
38382     getBox : function(){
38383         var b;
38384       //  if(!this.collapsed){
38385             b = this.el.getBox(false, true);
38386        // }else{
38387           //  b = this.collapsedEl.getBox(false, true);
38388         //}
38389         return b;
38390     },
38391
38392     getMargins : function(){
38393         return this.margins;
38394         //return this.collapsed ? this.cmargins : this.margins;
38395     },
38396 /*
38397     highlight : function(){
38398         this.el.addClass("x-layout-panel-dragover");
38399     },
38400
38401     unhighlight : function(){
38402         this.el.removeClass("x-layout-panel-dragover");
38403     },
38404 */
38405     updateBox : function(box)
38406     {
38407         if (!this.bodyEl) {
38408             return; // not rendered yet..
38409         }
38410         
38411         this.box = box;
38412         if(!this.collapsed){
38413             this.el.dom.style.left = box.x + "px";
38414             this.el.dom.style.top = box.y + "px";
38415             this.updateBody(box.width, box.height);
38416         }else{
38417             this.collapsedEl.dom.style.left = box.x + "px";
38418             this.collapsedEl.dom.style.top = box.y + "px";
38419             this.collapsedEl.setSize(box.width, box.height);
38420         }
38421         if(this.tabs){
38422             this.tabs.autoSizeTabs();
38423         }
38424     },
38425
38426     updateBody : function(w, h)
38427     {
38428         if(w !== null){
38429             this.el.setWidth(w);
38430             w -= this.el.getBorderWidth("rl");
38431             if(this.config.adjustments){
38432                 w += this.config.adjustments[0];
38433             }
38434         }
38435         if(h !== null && h > 0){
38436             this.el.setHeight(h);
38437             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38438             h -= this.el.getBorderWidth("tb");
38439             if(this.config.adjustments){
38440                 h += this.config.adjustments[1];
38441             }
38442             this.bodyEl.setHeight(h);
38443             if(this.tabs){
38444                 h = this.tabs.syncHeight(h);
38445             }
38446         }
38447         if(this.panelSize){
38448             w = w !== null ? w : this.panelSize.width;
38449             h = h !== null ? h : this.panelSize.height;
38450         }
38451         if(this.activePanel){
38452             var el = this.activePanel.getEl();
38453             w = w !== null ? w : el.getWidth();
38454             h = h !== null ? h : el.getHeight();
38455             this.panelSize = {width: w, height: h};
38456             this.activePanel.setSize(w, h);
38457         }
38458         if(Roo.isIE && this.tabs){
38459             this.tabs.el.repaint();
38460         }
38461     },
38462
38463     /**
38464      * Returns the container element for this region.
38465      * @return {Roo.Element}
38466      */
38467     getEl : function(){
38468         return this.el;
38469     },
38470
38471     /**
38472      * Hides this region.
38473      */
38474     hide : function(){
38475         //if(!this.collapsed){
38476             this.el.dom.style.left = "-2000px";
38477             this.el.hide();
38478         //}else{
38479          //   this.collapsedEl.dom.style.left = "-2000px";
38480          //   this.collapsedEl.hide();
38481        // }
38482         this.visible = false;
38483         this.fireEvent("visibilitychange", this, false);
38484     },
38485
38486     /**
38487      * Shows this region if it was previously hidden.
38488      */
38489     show : function(){
38490         //if(!this.collapsed){
38491             this.el.show();
38492         //}else{
38493         //    this.collapsedEl.show();
38494        // }
38495         this.visible = true;
38496         this.fireEvent("visibilitychange", this, true);
38497     },
38498 /*
38499     closeClicked : function(){
38500         if(this.activePanel){
38501             this.remove(this.activePanel);
38502         }
38503     },
38504
38505     collapseClick : function(e){
38506         if(this.isSlid){
38507            e.stopPropagation();
38508            this.slideIn();
38509         }else{
38510            e.stopPropagation();
38511            this.slideOut();
38512         }
38513     },
38514 */
38515     /**
38516      * Collapses this region.
38517      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38518      */
38519     /*
38520     collapse : function(skipAnim, skipCheck = false){
38521         if(this.collapsed) {
38522             return;
38523         }
38524         
38525         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38526             
38527             this.collapsed = true;
38528             if(this.split){
38529                 this.split.el.hide();
38530             }
38531             if(this.config.animate && skipAnim !== true){
38532                 this.fireEvent("invalidated", this);
38533                 this.animateCollapse();
38534             }else{
38535                 this.el.setLocation(-20000,-20000);
38536                 this.el.hide();
38537                 this.collapsedEl.show();
38538                 this.fireEvent("collapsed", this);
38539                 this.fireEvent("invalidated", this);
38540             }
38541         }
38542         
38543     },
38544 */
38545     animateCollapse : function(){
38546         // overridden
38547     },
38548
38549     /**
38550      * Expands this region if it was previously collapsed.
38551      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38552      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38553      */
38554     /*
38555     expand : function(e, skipAnim){
38556         if(e) {
38557             e.stopPropagation();
38558         }
38559         if(!this.collapsed || this.el.hasActiveFx()) {
38560             return;
38561         }
38562         if(this.isSlid){
38563             this.afterSlideIn();
38564             skipAnim = true;
38565         }
38566         this.collapsed = false;
38567         if(this.config.animate && skipAnim !== true){
38568             this.animateExpand();
38569         }else{
38570             this.el.show();
38571             if(this.split){
38572                 this.split.el.show();
38573             }
38574             this.collapsedEl.setLocation(-2000,-2000);
38575             this.collapsedEl.hide();
38576             this.fireEvent("invalidated", this);
38577             this.fireEvent("expanded", this);
38578         }
38579     },
38580 */
38581     animateExpand : function(){
38582         // overridden
38583     },
38584
38585     initTabs : function()
38586     {
38587         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38588         
38589         var ts = new Roo.bootstrap.panel.Tabs({
38590             el: this.bodyEl.dom,
38591             region : this,
38592             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38593             disableTooltips: this.config.disableTabTips,
38594             toolbar : this.config.toolbar
38595         });
38596         
38597         if(this.config.hideTabs){
38598             ts.stripWrap.setDisplayed(false);
38599         }
38600         this.tabs = ts;
38601         ts.resizeTabs = this.config.resizeTabs === true;
38602         ts.minTabWidth = this.config.minTabWidth || 40;
38603         ts.maxTabWidth = this.config.maxTabWidth || 250;
38604         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38605         ts.monitorResize = false;
38606         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38607         ts.bodyEl.addClass('roo-layout-tabs-body');
38608         this.panels.each(this.initPanelAsTab, this);
38609     },
38610
38611     initPanelAsTab : function(panel){
38612         var ti = this.tabs.addTab(
38613             panel.getEl().id,
38614             panel.getTitle(),
38615             null,
38616             this.config.closeOnTab && panel.isClosable(),
38617             panel.tpl
38618         );
38619         if(panel.tabTip !== undefined){
38620             ti.setTooltip(panel.tabTip);
38621         }
38622         ti.on("activate", function(){
38623               this.setActivePanel(panel);
38624         }, this);
38625         
38626         if(this.config.closeOnTab){
38627             ti.on("beforeclose", function(t, e){
38628                 e.cancel = true;
38629                 this.remove(panel);
38630             }, this);
38631         }
38632         
38633         panel.tabItem = ti;
38634         
38635         return ti;
38636     },
38637
38638     updatePanelTitle : function(panel, title)
38639     {
38640         if(this.activePanel == panel){
38641             this.updateTitle(title);
38642         }
38643         if(this.tabs){
38644             var ti = this.tabs.getTab(panel.getEl().id);
38645             ti.setText(title);
38646             if(panel.tabTip !== undefined){
38647                 ti.setTooltip(panel.tabTip);
38648             }
38649         }
38650     },
38651
38652     updateTitle : function(title){
38653         if(this.titleTextEl && !this.config.title){
38654             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38655         }
38656     },
38657
38658     setActivePanel : function(panel)
38659     {
38660         panel = this.getPanel(panel);
38661         if(this.activePanel && this.activePanel != panel){
38662             if(this.activePanel.setActiveState(false) === false){
38663                 return;
38664             }
38665         }
38666         this.activePanel = panel;
38667         panel.setActiveState(true);
38668         if(this.panelSize){
38669             panel.setSize(this.panelSize.width, this.panelSize.height);
38670         }
38671         if(this.closeBtn){
38672             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38673         }
38674         this.updateTitle(panel.getTitle());
38675         if(this.tabs){
38676             this.fireEvent("invalidated", this);
38677         }
38678         this.fireEvent("panelactivated", this, panel);
38679     },
38680
38681     /**
38682      * Shows the specified panel.
38683      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38684      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38685      */
38686     showPanel : function(panel)
38687     {
38688         panel = this.getPanel(panel);
38689         if(panel){
38690             if(this.tabs){
38691                 var tab = this.tabs.getTab(panel.getEl().id);
38692                 if(tab.isHidden()){
38693                     this.tabs.unhideTab(tab.id);
38694                 }
38695                 tab.activate();
38696             }else{
38697                 this.setActivePanel(panel);
38698             }
38699         }
38700         return panel;
38701     },
38702
38703     /**
38704      * Get the active panel for this region.
38705      * @return {Roo.ContentPanel} The active panel or null
38706      */
38707     getActivePanel : function(){
38708         return this.activePanel;
38709     },
38710
38711     validateVisibility : function(){
38712         if(this.panels.getCount() < 1){
38713             this.updateTitle("&#160;");
38714             this.closeBtn.hide();
38715             this.hide();
38716         }else{
38717             if(!this.isVisible()){
38718                 this.show();
38719             }
38720         }
38721     },
38722
38723     /**
38724      * Adds the passed ContentPanel(s) to this region.
38725      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38726      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38727      */
38728     add : function(panel)
38729     {
38730         if(arguments.length > 1){
38731             for(var i = 0, len = arguments.length; i < len; i++) {
38732                 this.add(arguments[i]);
38733             }
38734             return null;
38735         }
38736         
38737         // if we have not been rendered yet, then we can not really do much of this..
38738         if (!this.bodyEl) {
38739             this.unrendered_panels.push(panel);
38740             return panel;
38741         }
38742         
38743         
38744         
38745         
38746         if(this.hasPanel(panel)){
38747             this.showPanel(panel);
38748             return panel;
38749         }
38750         panel.setRegion(this);
38751         this.panels.add(panel);
38752        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38753             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38754             // and hide them... ???
38755             this.bodyEl.dom.appendChild(panel.getEl().dom);
38756             if(panel.background !== true){
38757                 this.setActivePanel(panel);
38758             }
38759             this.fireEvent("paneladded", this, panel);
38760             return panel;
38761         }
38762         */
38763         if(!this.tabs){
38764             this.initTabs();
38765         }else{
38766             this.initPanelAsTab(panel);
38767         }
38768         
38769         
38770         if(panel.background !== true){
38771             this.tabs.activate(panel.getEl().id);
38772         }
38773         this.fireEvent("paneladded", this, panel);
38774         return panel;
38775     },
38776
38777     /**
38778      * Hides the tab for the specified panel.
38779      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38780      */
38781     hidePanel : function(panel){
38782         if(this.tabs && (panel = this.getPanel(panel))){
38783             this.tabs.hideTab(panel.getEl().id);
38784         }
38785     },
38786
38787     /**
38788      * Unhides the tab for a previously hidden panel.
38789      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38790      */
38791     unhidePanel : function(panel){
38792         if(this.tabs && (panel = this.getPanel(panel))){
38793             this.tabs.unhideTab(panel.getEl().id);
38794         }
38795     },
38796
38797     clearPanels : function(){
38798         while(this.panels.getCount() > 0){
38799              this.remove(this.panels.first());
38800         }
38801     },
38802
38803     /**
38804      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38805      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38806      * @param {Boolean} preservePanel Overrides the config preservePanel option
38807      * @return {Roo.ContentPanel} The panel that was removed
38808      */
38809     remove : function(panel, preservePanel)
38810     {
38811         panel = this.getPanel(panel);
38812         if(!panel){
38813             return null;
38814         }
38815         var e = {};
38816         this.fireEvent("beforeremove", this, panel, e);
38817         if(e.cancel === true){
38818             return null;
38819         }
38820         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38821         var panelId = panel.getId();
38822         this.panels.removeKey(panelId);
38823         if(preservePanel){
38824             document.body.appendChild(panel.getEl().dom);
38825         }
38826         if(this.tabs){
38827             this.tabs.removeTab(panel.getEl().id);
38828         }else if (!preservePanel){
38829             this.bodyEl.dom.removeChild(panel.getEl().dom);
38830         }
38831         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38832             var p = this.panels.first();
38833             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38834             tempEl.appendChild(p.getEl().dom);
38835             this.bodyEl.update("");
38836             this.bodyEl.dom.appendChild(p.getEl().dom);
38837             tempEl = null;
38838             this.updateTitle(p.getTitle());
38839             this.tabs = null;
38840             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38841             this.setActivePanel(p);
38842         }
38843         panel.setRegion(null);
38844         if(this.activePanel == panel){
38845             this.activePanel = null;
38846         }
38847         if(this.config.autoDestroy !== false && preservePanel !== true){
38848             try{panel.destroy();}catch(e){}
38849         }
38850         this.fireEvent("panelremoved", this, panel);
38851         return panel;
38852     },
38853
38854     /**
38855      * Returns the TabPanel component used by this region
38856      * @return {Roo.TabPanel}
38857      */
38858     getTabs : function(){
38859         return this.tabs;
38860     },
38861
38862     createTool : function(parentEl, className){
38863         var btn = Roo.DomHelper.append(parentEl, {
38864             tag: "div",
38865             cls: "x-layout-tools-button",
38866             children: [ {
38867                 tag: "div",
38868                 cls: "roo-layout-tools-button-inner " + className,
38869                 html: "&#160;"
38870             }]
38871         }, true);
38872         btn.addClassOnOver("roo-layout-tools-button-over");
38873         return btn;
38874     }
38875 });/*
38876  * Based on:
38877  * Ext JS Library 1.1.1
38878  * Copyright(c) 2006-2007, Ext JS, LLC.
38879  *
38880  * Originally Released Under LGPL - original licence link has changed is not relivant.
38881  *
38882  * Fork - LGPL
38883  * <script type="text/javascript">
38884  */
38885  
38886
38887
38888 /**
38889  * @class Roo.SplitLayoutRegion
38890  * @extends Roo.LayoutRegion
38891  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38892  */
38893 Roo.bootstrap.layout.Split = function(config){
38894     this.cursor = config.cursor;
38895     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38896 };
38897
38898 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38899 {
38900     splitTip : "Drag to resize.",
38901     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38902     useSplitTips : false,
38903
38904     applyConfig : function(config){
38905         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38906     },
38907     
38908     onRender : function(ctr,pos) {
38909         
38910         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38911         if(!this.config.split){
38912             return;
38913         }
38914         if(!this.split){
38915             
38916             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38917                             tag: "div",
38918                             id: this.el.id + "-split",
38919                             cls: "roo-layout-split roo-layout-split-"+this.position,
38920                             html: "&#160;"
38921             });
38922             /** The SplitBar for this region 
38923             * @type Roo.SplitBar */
38924             // does not exist yet...
38925             Roo.log([this.position, this.orientation]);
38926             
38927             this.split = new Roo.bootstrap.SplitBar({
38928                 dragElement : splitEl,
38929                 resizingElement: this.el,
38930                 orientation : this.orientation
38931             });
38932             
38933             this.split.on("moved", this.onSplitMove, this);
38934             this.split.useShim = this.config.useShim === true;
38935             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38936             if(this.useSplitTips){
38937                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38938             }
38939             //if(config.collapsible){
38940             //    this.split.el.on("dblclick", this.collapse,  this);
38941             //}
38942         }
38943         if(typeof this.config.minSize != "undefined"){
38944             this.split.minSize = this.config.minSize;
38945         }
38946         if(typeof this.config.maxSize != "undefined"){
38947             this.split.maxSize = this.config.maxSize;
38948         }
38949         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38950             this.hideSplitter();
38951         }
38952         
38953     },
38954
38955     getHMaxSize : function(){
38956          var cmax = this.config.maxSize || 10000;
38957          var center = this.mgr.getRegion("center");
38958          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38959     },
38960
38961     getVMaxSize : function(){
38962          var cmax = this.config.maxSize || 10000;
38963          var center = this.mgr.getRegion("center");
38964          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38965     },
38966
38967     onSplitMove : function(split, newSize){
38968         this.fireEvent("resized", this, newSize);
38969     },
38970     
38971     /** 
38972      * Returns the {@link Roo.SplitBar} for this region.
38973      * @return {Roo.SplitBar}
38974      */
38975     getSplitBar : function(){
38976         return this.split;
38977     },
38978     
38979     hide : function(){
38980         this.hideSplitter();
38981         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38982     },
38983
38984     hideSplitter : function(){
38985         if(this.split){
38986             this.split.el.setLocation(-2000,-2000);
38987             this.split.el.hide();
38988         }
38989     },
38990
38991     show : function(){
38992         if(this.split){
38993             this.split.el.show();
38994         }
38995         Roo.bootstrap.layout.Split.superclass.show.call(this);
38996     },
38997     
38998     beforeSlide: function(){
38999         if(Roo.isGecko){// firefox overflow auto bug workaround
39000             this.bodyEl.clip();
39001             if(this.tabs) {
39002                 this.tabs.bodyEl.clip();
39003             }
39004             if(this.activePanel){
39005                 this.activePanel.getEl().clip();
39006                 
39007                 if(this.activePanel.beforeSlide){
39008                     this.activePanel.beforeSlide();
39009                 }
39010             }
39011         }
39012     },
39013     
39014     afterSlide : function(){
39015         if(Roo.isGecko){// firefox overflow auto bug workaround
39016             this.bodyEl.unclip();
39017             if(this.tabs) {
39018                 this.tabs.bodyEl.unclip();
39019             }
39020             if(this.activePanel){
39021                 this.activePanel.getEl().unclip();
39022                 if(this.activePanel.afterSlide){
39023                     this.activePanel.afterSlide();
39024                 }
39025             }
39026         }
39027     },
39028
39029     initAutoHide : function(){
39030         if(this.autoHide !== false){
39031             if(!this.autoHideHd){
39032                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39033                 this.autoHideHd = {
39034                     "mouseout": function(e){
39035                         if(!e.within(this.el, true)){
39036                             st.delay(500);
39037                         }
39038                     },
39039                     "mouseover" : function(e){
39040                         st.cancel();
39041                     },
39042                     scope : this
39043                 };
39044             }
39045             this.el.on(this.autoHideHd);
39046         }
39047     },
39048
39049     clearAutoHide : function(){
39050         if(this.autoHide !== false){
39051             this.el.un("mouseout", this.autoHideHd.mouseout);
39052             this.el.un("mouseover", this.autoHideHd.mouseover);
39053         }
39054     },
39055
39056     clearMonitor : function(){
39057         Roo.get(document).un("click", this.slideInIf, this);
39058     },
39059
39060     // these names are backwards but not changed for compat
39061     slideOut : function(){
39062         if(this.isSlid || this.el.hasActiveFx()){
39063             return;
39064         }
39065         this.isSlid = true;
39066         if(this.collapseBtn){
39067             this.collapseBtn.hide();
39068         }
39069         this.closeBtnState = this.closeBtn.getStyle('display');
39070         this.closeBtn.hide();
39071         if(this.stickBtn){
39072             this.stickBtn.show();
39073         }
39074         this.el.show();
39075         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39076         this.beforeSlide();
39077         this.el.setStyle("z-index", 10001);
39078         this.el.slideIn(this.getSlideAnchor(), {
39079             callback: function(){
39080                 this.afterSlide();
39081                 this.initAutoHide();
39082                 Roo.get(document).on("click", this.slideInIf, this);
39083                 this.fireEvent("slideshow", this);
39084             },
39085             scope: this,
39086             block: true
39087         });
39088     },
39089
39090     afterSlideIn : function(){
39091         this.clearAutoHide();
39092         this.isSlid = false;
39093         this.clearMonitor();
39094         this.el.setStyle("z-index", "");
39095         if(this.collapseBtn){
39096             this.collapseBtn.show();
39097         }
39098         this.closeBtn.setStyle('display', this.closeBtnState);
39099         if(this.stickBtn){
39100             this.stickBtn.hide();
39101         }
39102         this.fireEvent("slidehide", this);
39103     },
39104
39105     slideIn : function(cb){
39106         if(!this.isSlid || this.el.hasActiveFx()){
39107             Roo.callback(cb);
39108             return;
39109         }
39110         this.isSlid = false;
39111         this.beforeSlide();
39112         this.el.slideOut(this.getSlideAnchor(), {
39113             callback: function(){
39114                 this.el.setLeftTop(-10000, -10000);
39115                 this.afterSlide();
39116                 this.afterSlideIn();
39117                 Roo.callback(cb);
39118             },
39119             scope: this,
39120             block: true
39121         });
39122     },
39123     
39124     slideInIf : function(e){
39125         if(!e.within(this.el)){
39126             this.slideIn();
39127         }
39128     },
39129
39130     animateCollapse : function(){
39131         this.beforeSlide();
39132         this.el.setStyle("z-index", 20000);
39133         var anchor = this.getSlideAnchor();
39134         this.el.slideOut(anchor, {
39135             callback : function(){
39136                 this.el.setStyle("z-index", "");
39137                 this.collapsedEl.slideIn(anchor, {duration:.3});
39138                 this.afterSlide();
39139                 this.el.setLocation(-10000,-10000);
39140                 this.el.hide();
39141                 this.fireEvent("collapsed", this);
39142             },
39143             scope: this,
39144             block: true
39145         });
39146     },
39147
39148     animateExpand : function(){
39149         this.beforeSlide();
39150         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39151         this.el.setStyle("z-index", 20000);
39152         this.collapsedEl.hide({
39153             duration:.1
39154         });
39155         this.el.slideIn(this.getSlideAnchor(), {
39156             callback : function(){
39157                 this.el.setStyle("z-index", "");
39158                 this.afterSlide();
39159                 if(this.split){
39160                     this.split.el.show();
39161                 }
39162                 this.fireEvent("invalidated", this);
39163                 this.fireEvent("expanded", this);
39164             },
39165             scope: this,
39166             block: true
39167         });
39168     },
39169
39170     anchors : {
39171         "west" : "left",
39172         "east" : "right",
39173         "north" : "top",
39174         "south" : "bottom"
39175     },
39176
39177     sanchors : {
39178         "west" : "l",
39179         "east" : "r",
39180         "north" : "t",
39181         "south" : "b"
39182     },
39183
39184     canchors : {
39185         "west" : "tl-tr",
39186         "east" : "tr-tl",
39187         "north" : "tl-bl",
39188         "south" : "bl-tl"
39189     },
39190
39191     getAnchor : function(){
39192         return this.anchors[this.position];
39193     },
39194
39195     getCollapseAnchor : function(){
39196         return this.canchors[this.position];
39197     },
39198
39199     getSlideAnchor : function(){
39200         return this.sanchors[this.position];
39201     },
39202
39203     getAlignAdj : function(){
39204         var cm = this.cmargins;
39205         switch(this.position){
39206             case "west":
39207                 return [0, 0];
39208             break;
39209             case "east":
39210                 return [0, 0];
39211             break;
39212             case "north":
39213                 return [0, 0];
39214             break;
39215             case "south":
39216                 return [0, 0];
39217             break;
39218         }
39219     },
39220
39221     getExpandAdj : function(){
39222         var c = this.collapsedEl, cm = this.cmargins;
39223         switch(this.position){
39224             case "west":
39225                 return [-(cm.right+c.getWidth()+cm.left), 0];
39226             break;
39227             case "east":
39228                 return [cm.right+c.getWidth()+cm.left, 0];
39229             break;
39230             case "north":
39231                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39232             break;
39233             case "south":
39234                 return [0, cm.top+cm.bottom+c.getHeight()];
39235             break;
39236         }
39237     }
39238 });/*
39239  * Based on:
39240  * Ext JS Library 1.1.1
39241  * Copyright(c) 2006-2007, Ext JS, LLC.
39242  *
39243  * Originally Released Under LGPL - original licence link has changed is not relivant.
39244  *
39245  * Fork - LGPL
39246  * <script type="text/javascript">
39247  */
39248 /*
39249  * These classes are private internal classes
39250  */
39251 Roo.bootstrap.layout.Center = function(config){
39252     config.region = "center";
39253     Roo.bootstrap.layout.Region.call(this, config);
39254     this.visible = true;
39255     this.minWidth = config.minWidth || 20;
39256     this.minHeight = config.minHeight || 20;
39257 };
39258
39259 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39260     hide : function(){
39261         // center panel can't be hidden
39262     },
39263     
39264     show : function(){
39265         // center panel can't be hidden
39266     },
39267     
39268     getMinWidth: function(){
39269         return this.minWidth;
39270     },
39271     
39272     getMinHeight: function(){
39273         return this.minHeight;
39274     }
39275 });
39276
39277
39278
39279
39280  
39281
39282
39283
39284
39285
39286
39287 Roo.bootstrap.layout.North = function(config)
39288 {
39289     config.region = 'north';
39290     config.cursor = 'n-resize';
39291     
39292     Roo.bootstrap.layout.Split.call(this, config);
39293     
39294     
39295     if(this.split){
39296         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39297         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39298         this.split.el.addClass("roo-layout-split-v");
39299     }
39300     //var size = config.initialSize || config.height;
39301     //if(this.el && typeof size != "undefined"){
39302     //    this.el.setHeight(size);
39303     //}
39304 };
39305 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39306 {
39307     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39308      
39309      
39310     onRender : function(ctr, pos)
39311     {
39312         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39313         var size = this.config.initialSize || this.config.height;
39314         if(this.el && typeof size != "undefined"){
39315             this.el.setHeight(size);
39316         }
39317     
39318     },
39319     
39320     getBox : function(){
39321         if(this.collapsed){
39322             return this.collapsedEl.getBox();
39323         }
39324         var box = this.el.getBox();
39325         if(this.split){
39326             box.height += this.split.el.getHeight();
39327         }
39328         return box;
39329     },
39330     
39331     updateBox : function(box){
39332         if(this.split && !this.collapsed){
39333             box.height -= this.split.el.getHeight();
39334             this.split.el.setLeft(box.x);
39335             this.split.el.setTop(box.y+box.height);
39336             this.split.el.setWidth(box.width);
39337         }
39338         if(this.collapsed){
39339             this.updateBody(box.width, null);
39340         }
39341         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39342     }
39343 });
39344
39345
39346
39347
39348
39349 Roo.bootstrap.layout.South = function(config){
39350     config.region = 'south';
39351     config.cursor = 's-resize';
39352     Roo.bootstrap.layout.Split.call(this, config);
39353     if(this.split){
39354         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39355         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39356         this.split.el.addClass("roo-layout-split-v");
39357     }
39358     
39359 };
39360
39361 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39362     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39363     
39364     onRender : function(ctr, pos)
39365     {
39366         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39367         var size = this.config.initialSize || this.config.height;
39368         if(this.el && typeof size != "undefined"){
39369             this.el.setHeight(size);
39370         }
39371     
39372     },
39373     
39374     getBox : function(){
39375         if(this.collapsed){
39376             return this.collapsedEl.getBox();
39377         }
39378         var box = this.el.getBox();
39379         if(this.split){
39380             var sh = this.split.el.getHeight();
39381             box.height += sh;
39382             box.y -= sh;
39383         }
39384         return box;
39385     },
39386     
39387     updateBox : function(box){
39388         if(this.split && !this.collapsed){
39389             var sh = this.split.el.getHeight();
39390             box.height -= sh;
39391             box.y += sh;
39392             this.split.el.setLeft(box.x);
39393             this.split.el.setTop(box.y-sh);
39394             this.split.el.setWidth(box.width);
39395         }
39396         if(this.collapsed){
39397             this.updateBody(box.width, null);
39398         }
39399         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39400     }
39401 });
39402
39403 Roo.bootstrap.layout.East = function(config){
39404     config.region = "east";
39405     config.cursor = "e-resize";
39406     Roo.bootstrap.layout.Split.call(this, config);
39407     if(this.split){
39408         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39409         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39410         this.split.el.addClass("roo-layout-split-h");
39411     }
39412     
39413 };
39414 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39415     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39416     
39417     onRender : function(ctr, pos)
39418     {
39419         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39420         var size = this.config.initialSize || this.config.width;
39421         if(this.el && typeof size != "undefined"){
39422             this.el.setWidth(size);
39423         }
39424     
39425     },
39426     
39427     getBox : function(){
39428         if(this.collapsed){
39429             return this.collapsedEl.getBox();
39430         }
39431         var box = this.el.getBox();
39432         if(this.split){
39433             var sw = this.split.el.getWidth();
39434             box.width += sw;
39435             box.x -= sw;
39436         }
39437         return box;
39438     },
39439
39440     updateBox : function(box){
39441         if(this.split && !this.collapsed){
39442             var sw = this.split.el.getWidth();
39443             box.width -= sw;
39444             this.split.el.setLeft(box.x);
39445             this.split.el.setTop(box.y);
39446             this.split.el.setHeight(box.height);
39447             box.x += sw;
39448         }
39449         if(this.collapsed){
39450             this.updateBody(null, box.height);
39451         }
39452         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39453     }
39454 });
39455
39456 Roo.bootstrap.layout.West = function(config){
39457     config.region = "west";
39458     config.cursor = "w-resize";
39459     
39460     Roo.bootstrap.layout.Split.call(this, config);
39461     if(this.split){
39462         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39463         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39464         this.split.el.addClass("roo-layout-split-h");
39465     }
39466     
39467 };
39468 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39469     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39470     
39471     onRender: function(ctr, pos)
39472     {
39473         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39474         var size = this.config.initialSize || this.config.width;
39475         if(typeof size != "undefined"){
39476             this.el.setWidth(size);
39477         }
39478     },
39479     
39480     getBox : function(){
39481         if(this.collapsed){
39482             return this.collapsedEl.getBox();
39483         }
39484         var box = this.el.getBox();
39485         if (box.width == 0) {
39486             box.width = this.config.width; // kludge?
39487         }
39488         if(this.split){
39489             box.width += this.split.el.getWidth();
39490         }
39491         return box;
39492     },
39493     
39494     updateBox : function(box){
39495         if(this.split && !this.collapsed){
39496             var sw = this.split.el.getWidth();
39497             box.width -= sw;
39498             this.split.el.setLeft(box.x+box.width);
39499             this.split.el.setTop(box.y);
39500             this.split.el.setHeight(box.height);
39501         }
39502         if(this.collapsed){
39503             this.updateBody(null, box.height);
39504         }
39505         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39506     }
39507 });Roo.namespace("Roo.bootstrap.panel");/*
39508  * Based on:
39509  * Ext JS Library 1.1.1
39510  * Copyright(c) 2006-2007, Ext JS, LLC.
39511  *
39512  * Originally Released Under LGPL - original licence link has changed is not relivant.
39513  *
39514  * Fork - LGPL
39515  * <script type="text/javascript">
39516  */
39517 /**
39518  * @class Roo.ContentPanel
39519  * @extends Roo.util.Observable
39520  * A basic ContentPanel element.
39521  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39522  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39523  * @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
39524  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39525  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39526  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39527  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39528  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39529  * @cfg {String} title          The title for this panel
39530  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39531  * @cfg {String} url            Calls {@link #setUrl} with this value
39532  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39533  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39534  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39535  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39536  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39537  * @cfg {Boolean} badges render the badges
39538  * @cfg {String} cls  extra classes to use  
39539  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39540
39541  * @constructor
39542  * Create a new ContentPanel.
39543  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39544  * @param {String/Object} config A string to set only the title or a config object
39545  * @param {String} content (optional) Set the HTML content for this panel
39546  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39547  */
39548 Roo.bootstrap.panel.Content = function( config){
39549     
39550     this.tpl = config.tpl || false;
39551     
39552     var el = config.el;
39553     var content = config.content;
39554
39555     if(config.autoCreate){ // xtype is available if this is called from factory
39556         el = Roo.id();
39557     }
39558     this.el = Roo.get(el);
39559     if(!this.el && config && config.autoCreate){
39560         if(typeof config.autoCreate == "object"){
39561             if(!config.autoCreate.id){
39562                 config.autoCreate.id = config.id||el;
39563             }
39564             this.el = Roo.DomHelper.append(document.body,
39565                         config.autoCreate, true);
39566         }else{
39567             var elcfg =  {
39568                 tag: "div",
39569                 cls: (config.cls || '') +
39570                     (config.background ? ' bg-' + config.background : '') +
39571                     " roo-layout-inactive-content",
39572                 id: config.id||el
39573             };
39574             if (config.iframe) {
39575                 elcfg.cn = [
39576                     {
39577                         tag : 'iframe',
39578                         style : 'border: 0px',
39579                         src : 'about:blank'
39580                     }
39581                 ];
39582             }
39583               
39584             if (config.html) {
39585                 elcfg.html = config.html;
39586                 
39587             }
39588                         
39589             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39590             if (config.iframe) {
39591                 this.iframeEl = this.el.select('iframe',true).first();
39592             }
39593             
39594         }
39595     } 
39596     this.closable = false;
39597     this.loaded = false;
39598     this.active = false;
39599    
39600       
39601     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39602         
39603         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39604         
39605         this.wrapEl = this.el; //this.el.wrap();
39606         var ti = [];
39607         if (config.toolbar.items) {
39608             ti = config.toolbar.items ;
39609             delete config.toolbar.items ;
39610         }
39611         
39612         var nitems = [];
39613         this.toolbar.render(this.wrapEl, 'before');
39614         for(var i =0;i < ti.length;i++) {
39615           //  Roo.log(['add child', items[i]]);
39616             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39617         }
39618         this.toolbar.items = nitems;
39619         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39620         delete config.toolbar;
39621         
39622     }
39623     /*
39624     // xtype created footer. - not sure if will work as we normally have to render first..
39625     if (this.footer && !this.footer.el && this.footer.xtype) {
39626         if (!this.wrapEl) {
39627             this.wrapEl = this.el.wrap();
39628         }
39629     
39630         this.footer.container = this.wrapEl.createChild();
39631          
39632         this.footer = Roo.factory(this.footer, Roo);
39633         
39634     }
39635     */
39636     
39637      if(typeof config == "string"){
39638         this.title = config;
39639     }else{
39640         Roo.apply(this, config);
39641     }
39642     
39643     if(this.resizeEl){
39644         this.resizeEl = Roo.get(this.resizeEl, true);
39645     }else{
39646         this.resizeEl = this.el;
39647     }
39648     // handle view.xtype
39649     
39650  
39651     
39652     
39653     this.addEvents({
39654         /**
39655          * @event activate
39656          * Fires when this panel is activated. 
39657          * @param {Roo.ContentPanel} this
39658          */
39659         "activate" : true,
39660         /**
39661          * @event deactivate
39662          * Fires when this panel is activated. 
39663          * @param {Roo.ContentPanel} this
39664          */
39665         "deactivate" : true,
39666
39667         /**
39668          * @event resize
39669          * Fires when this panel is resized if fitToFrame is true.
39670          * @param {Roo.ContentPanel} this
39671          * @param {Number} width The width after any component adjustments
39672          * @param {Number} height The height after any component adjustments
39673          */
39674         "resize" : true,
39675         
39676          /**
39677          * @event render
39678          * Fires when this tab is created
39679          * @param {Roo.ContentPanel} this
39680          */
39681         "render" : true
39682         
39683         
39684         
39685     });
39686     
39687
39688     
39689     
39690     if(this.autoScroll && !this.iframe){
39691         this.resizeEl.setStyle("overflow", "auto");
39692     } else {
39693         // fix randome scrolling
39694         //this.el.on('scroll', function() {
39695         //    Roo.log('fix random scolling');
39696         //    this.scrollTo('top',0); 
39697         //});
39698     }
39699     content = content || this.content;
39700     if(content){
39701         this.setContent(content);
39702     }
39703     if(config && config.url){
39704         this.setUrl(this.url, this.params, this.loadOnce);
39705     }
39706     
39707     
39708     
39709     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39710     
39711     if (this.view && typeof(this.view.xtype) != 'undefined') {
39712         this.view.el = this.el.appendChild(document.createElement("div"));
39713         this.view = Roo.factory(this.view); 
39714         this.view.render  &&  this.view.render(false, '');  
39715     }
39716     
39717     
39718     this.fireEvent('render', this);
39719 };
39720
39721 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39722     
39723     cls : '',
39724     background : '',
39725     
39726     tabTip : '',
39727     
39728     iframe : false,
39729     iframeEl : false,
39730     
39731     setRegion : function(region){
39732         this.region = region;
39733         this.setActiveClass(region && !this.background);
39734     },
39735     
39736     
39737     setActiveClass: function(state)
39738     {
39739         if(state){
39740            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39741            this.el.setStyle('position','relative');
39742         }else{
39743            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39744            this.el.setStyle('position', 'absolute');
39745         } 
39746     },
39747     
39748     /**
39749      * Returns the toolbar for this Panel if one was configured. 
39750      * @return {Roo.Toolbar} 
39751      */
39752     getToolbar : function(){
39753         return this.toolbar;
39754     },
39755     
39756     setActiveState : function(active)
39757     {
39758         this.active = active;
39759         this.setActiveClass(active);
39760         if(!active){
39761             if(this.fireEvent("deactivate", this) === false){
39762                 return false;
39763             }
39764             return true;
39765         }
39766         this.fireEvent("activate", this);
39767         return true;
39768     },
39769     /**
39770      * Updates this panel's element (not for iframe)
39771      * @param {String} content The new content
39772      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39773     */
39774     setContent : function(content, loadScripts){
39775         if (this.iframe) {
39776             return;
39777         }
39778         
39779         this.el.update(content, loadScripts);
39780     },
39781
39782     ignoreResize : function(w, h){
39783         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39784             return true;
39785         }else{
39786             this.lastSize = {width: w, height: h};
39787             return false;
39788         }
39789     },
39790     /**
39791      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39792      * @return {Roo.UpdateManager} The UpdateManager
39793      */
39794     getUpdateManager : function(){
39795         if (this.iframe) {
39796             return false;
39797         }
39798         return this.el.getUpdateManager();
39799     },
39800      /**
39801      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39802      * Does not work with IFRAME contents
39803      * @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:
39804 <pre><code>
39805 panel.load({
39806     url: "your-url.php",
39807     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39808     callback: yourFunction,
39809     scope: yourObject, //(optional scope)
39810     discardUrl: false,
39811     nocache: false,
39812     text: "Loading...",
39813     timeout: 30,
39814     scripts: false
39815 });
39816 </code></pre>
39817      
39818      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39819      * 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.
39820      * @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}
39821      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39822      * @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.
39823      * @return {Roo.ContentPanel} this
39824      */
39825     load : function(){
39826         
39827         if (this.iframe) {
39828             return this;
39829         }
39830         
39831         var um = this.el.getUpdateManager();
39832         um.update.apply(um, arguments);
39833         return this;
39834     },
39835
39836
39837     /**
39838      * 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.
39839      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39840      * @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)
39841      * @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)
39842      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39843      */
39844     setUrl : function(url, params, loadOnce){
39845         if (this.iframe) {
39846             this.iframeEl.dom.src = url;
39847             return false;
39848         }
39849         
39850         if(this.refreshDelegate){
39851             this.removeListener("activate", this.refreshDelegate);
39852         }
39853         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39854         this.on("activate", this.refreshDelegate);
39855         return this.el.getUpdateManager();
39856     },
39857     
39858     _handleRefresh : function(url, params, loadOnce){
39859         if(!loadOnce || !this.loaded){
39860             var updater = this.el.getUpdateManager();
39861             updater.update(url, params, this._setLoaded.createDelegate(this));
39862         }
39863     },
39864     
39865     _setLoaded : function(){
39866         this.loaded = true;
39867     }, 
39868     
39869     /**
39870      * Returns this panel's id
39871      * @return {String} 
39872      */
39873     getId : function(){
39874         return this.el.id;
39875     },
39876     
39877     /** 
39878      * Returns this panel's element - used by regiosn to add.
39879      * @return {Roo.Element} 
39880      */
39881     getEl : function(){
39882         return this.wrapEl || this.el;
39883     },
39884     
39885    
39886     
39887     adjustForComponents : function(width, height)
39888     {
39889         //Roo.log('adjustForComponents ');
39890         if(this.resizeEl != this.el){
39891             width -= this.el.getFrameWidth('lr');
39892             height -= this.el.getFrameWidth('tb');
39893         }
39894         if(this.toolbar){
39895             var te = this.toolbar.getEl();
39896             te.setWidth(width);
39897             height -= te.getHeight();
39898         }
39899         if(this.footer){
39900             var te = this.footer.getEl();
39901             te.setWidth(width);
39902             height -= te.getHeight();
39903         }
39904         
39905         
39906         if(this.adjustments){
39907             width += this.adjustments[0];
39908             height += this.adjustments[1];
39909         }
39910         return {"width": width, "height": height};
39911     },
39912     
39913     setSize : function(width, height){
39914         if(this.fitToFrame && !this.ignoreResize(width, height)){
39915             if(this.fitContainer && this.resizeEl != this.el){
39916                 this.el.setSize(width, height);
39917             }
39918             var size = this.adjustForComponents(width, height);
39919             if (this.iframe) {
39920                 this.iframeEl.setSize(width,height);
39921             }
39922             
39923             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39924             this.fireEvent('resize', this, size.width, size.height);
39925             
39926             
39927         }
39928     },
39929     
39930     /**
39931      * Returns this panel's title
39932      * @return {String} 
39933      */
39934     getTitle : function(){
39935         
39936         if (typeof(this.title) != 'object') {
39937             return this.title;
39938         }
39939         
39940         var t = '';
39941         for (var k in this.title) {
39942             if (!this.title.hasOwnProperty(k)) {
39943                 continue;
39944             }
39945             
39946             if (k.indexOf('-') >= 0) {
39947                 var s = k.split('-');
39948                 for (var i = 0; i<s.length; i++) {
39949                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39950                 }
39951             } else {
39952                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39953             }
39954         }
39955         return t;
39956     },
39957     
39958     /**
39959      * Set this panel's title
39960      * @param {String} title
39961      */
39962     setTitle : function(title){
39963         this.title = title;
39964         if(this.region){
39965             this.region.updatePanelTitle(this, title);
39966         }
39967     },
39968     
39969     /**
39970      * Returns true is this panel was configured to be closable
39971      * @return {Boolean} 
39972      */
39973     isClosable : function(){
39974         return this.closable;
39975     },
39976     
39977     beforeSlide : function(){
39978         this.el.clip();
39979         this.resizeEl.clip();
39980     },
39981     
39982     afterSlide : function(){
39983         this.el.unclip();
39984         this.resizeEl.unclip();
39985     },
39986     
39987     /**
39988      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39989      *   Will fail silently if the {@link #setUrl} method has not been called.
39990      *   This does not activate the panel, just updates its content.
39991      */
39992     refresh : function(){
39993         if(this.refreshDelegate){
39994            this.loaded = false;
39995            this.refreshDelegate();
39996         }
39997     },
39998     
39999     /**
40000      * Destroys this panel
40001      */
40002     destroy : function(){
40003         this.el.removeAllListeners();
40004         var tempEl = document.createElement("span");
40005         tempEl.appendChild(this.el.dom);
40006         tempEl.innerHTML = "";
40007         this.el.remove();
40008         this.el = null;
40009     },
40010     
40011     /**
40012      * form - if the content panel contains a form - this is a reference to it.
40013      * @type {Roo.form.Form}
40014      */
40015     form : false,
40016     /**
40017      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40018      *    This contains a reference to it.
40019      * @type {Roo.View}
40020      */
40021     view : false,
40022     
40023       /**
40024      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40025      * <pre><code>
40026
40027 layout.addxtype({
40028        xtype : 'Form',
40029        items: [ .... ]
40030    }
40031 );
40032
40033 </code></pre>
40034      * @param {Object} cfg Xtype definition of item to add.
40035      */
40036     
40037     
40038     getChildContainer: function () {
40039         return this.getEl();
40040     }
40041     
40042     
40043     /*
40044         var  ret = new Roo.factory(cfg);
40045         return ret;
40046         
40047         
40048         // add form..
40049         if (cfg.xtype.match(/^Form$/)) {
40050             
40051             var el;
40052             //if (this.footer) {
40053             //    el = this.footer.container.insertSibling(false, 'before');
40054             //} else {
40055                 el = this.el.createChild();
40056             //}
40057
40058             this.form = new  Roo.form.Form(cfg);
40059             
40060             
40061             if ( this.form.allItems.length) {
40062                 this.form.render(el.dom);
40063             }
40064             return this.form;
40065         }
40066         // should only have one of theses..
40067         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40068             // views.. should not be just added - used named prop 'view''
40069             
40070             cfg.el = this.el.appendChild(document.createElement("div"));
40071             // factory?
40072             
40073             var ret = new Roo.factory(cfg);
40074              
40075              ret.render && ret.render(false, ''); // render blank..
40076             this.view = ret;
40077             return ret;
40078         }
40079         return false;
40080     }
40081     \*/
40082 });
40083  
40084 /**
40085  * @class Roo.bootstrap.panel.Grid
40086  * @extends Roo.bootstrap.panel.Content
40087  * @constructor
40088  * Create a new GridPanel.
40089  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40090  * @param {Object} config A the config object
40091   
40092  */
40093
40094
40095
40096 Roo.bootstrap.panel.Grid = function(config)
40097 {
40098     
40099       
40100     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40101         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40102
40103     config.el = this.wrapper;
40104     //this.el = this.wrapper;
40105     
40106       if (config.container) {
40107         // ctor'ed from a Border/panel.grid
40108         
40109         
40110         this.wrapper.setStyle("overflow", "hidden");
40111         this.wrapper.addClass('roo-grid-container');
40112
40113     }
40114     
40115     
40116     if(config.toolbar){
40117         var tool_el = this.wrapper.createChild();    
40118         this.toolbar = Roo.factory(config.toolbar);
40119         var ti = [];
40120         if (config.toolbar.items) {
40121             ti = config.toolbar.items ;
40122             delete config.toolbar.items ;
40123         }
40124         
40125         var nitems = [];
40126         this.toolbar.render(tool_el);
40127         for(var i =0;i < ti.length;i++) {
40128           //  Roo.log(['add child', items[i]]);
40129             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40130         }
40131         this.toolbar.items = nitems;
40132         
40133         delete config.toolbar;
40134     }
40135     
40136     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40137     config.grid.scrollBody = true;;
40138     config.grid.monitorWindowResize = false; // turn off autosizing
40139     config.grid.autoHeight = false;
40140     config.grid.autoWidth = false;
40141     
40142     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40143     
40144     if (config.background) {
40145         // render grid on panel activation (if panel background)
40146         this.on('activate', function(gp) {
40147             if (!gp.grid.rendered) {
40148                 gp.grid.render(this.wrapper);
40149                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40150             }
40151         });
40152             
40153     } else {
40154         this.grid.render(this.wrapper);
40155         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40156
40157     }
40158     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40159     // ??? needed ??? config.el = this.wrapper;
40160     
40161     
40162     
40163   
40164     // xtype created footer. - not sure if will work as we normally have to render first..
40165     if (this.footer && !this.footer.el && this.footer.xtype) {
40166         
40167         var ctr = this.grid.getView().getFooterPanel(true);
40168         this.footer.dataSource = this.grid.dataSource;
40169         this.footer = Roo.factory(this.footer, Roo);
40170         this.footer.render(ctr);
40171         
40172     }
40173     
40174     
40175     
40176     
40177      
40178 };
40179
40180 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40181     getId : function(){
40182         return this.grid.id;
40183     },
40184     
40185     /**
40186      * Returns the grid for this panel
40187      * @return {Roo.bootstrap.Table} 
40188      */
40189     getGrid : function(){
40190         return this.grid;    
40191     },
40192     
40193     setSize : function(width, height){
40194         if(!this.ignoreResize(width, height)){
40195             var grid = this.grid;
40196             var size = this.adjustForComponents(width, height);
40197             // tfoot is not a footer?
40198           
40199             
40200             var gridel = grid.getGridEl();
40201             gridel.setSize(size.width, size.height);
40202             
40203             var tbd = grid.getGridEl().select('tbody', true).first();
40204             var thd = grid.getGridEl().select('thead',true).first();
40205             var tbf= grid.getGridEl().select('tfoot', true).first();
40206
40207             if (tbf) {
40208                 size.height -= tbf.getHeight();
40209             }
40210             if (thd) {
40211                 size.height -= thd.getHeight();
40212             }
40213             
40214             tbd.setSize(size.width, size.height );
40215             // this is for the account management tab -seems to work there.
40216             var thd = grid.getGridEl().select('thead',true).first();
40217             //if (tbd) {
40218             //    tbd.setSize(size.width, size.height - thd.getHeight());
40219             //}
40220              
40221             grid.autoSize();
40222         }
40223     },
40224      
40225     
40226     
40227     beforeSlide : function(){
40228         this.grid.getView().scroller.clip();
40229     },
40230     
40231     afterSlide : function(){
40232         this.grid.getView().scroller.unclip();
40233     },
40234     
40235     destroy : function(){
40236         this.grid.destroy();
40237         delete this.grid;
40238         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40239     }
40240 });
40241
40242 /**
40243  * @class Roo.bootstrap.panel.Nest
40244  * @extends Roo.bootstrap.panel.Content
40245  * @constructor
40246  * Create a new Panel, that can contain a layout.Border.
40247  * 
40248  * 
40249  * @param {Roo.BorderLayout} layout The layout for this panel
40250  * @param {String/Object} config A string to set only the title or a config object
40251  */
40252 Roo.bootstrap.panel.Nest = function(config)
40253 {
40254     // construct with only one argument..
40255     /* FIXME - implement nicer consturctors
40256     if (layout.layout) {
40257         config = layout;
40258         layout = config.layout;
40259         delete config.layout;
40260     }
40261     if (layout.xtype && !layout.getEl) {
40262         // then layout needs constructing..
40263         layout = Roo.factory(layout, Roo);
40264     }
40265     */
40266     
40267     config.el =  config.layout.getEl();
40268     
40269     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40270     
40271     config.layout.monitorWindowResize = false; // turn off autosizing
40272     this.layout = config.layout;
40273     this.layout.getEl().addClass("roo-layout-nested-layout");
40274     this.layout.parent = this;
40275     
40276     
40277     
40278     
40279 };
40280
40281 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40282
40283     setSize : function(width, height){
40284         if(!this.ignoreResize(width, height)){
40285             var size = this.adjustForComponents(width, height);
40286             var el = this.layout.getEl();
40287             if (size.height < 1) {
40288                 el.setWidth(size.width);   
40289             } else {
40290                 el.setSize(size.width, size.height);
40291             }
40292             var touch = el.dom.offsetWidth;
40293             this.layout.layout();
40294             // ie requires a double layout on the first pass
40295             if(Roo.isIE && !this.initialized){
40296                 this.initialized = true;
40297                 this.layout.layout();
40298             }
40299         }
40300     },
40301     
40302     // activate all subpanels if not currently active..
40303     
40304     setActiveState : function(active){
40305         this.active = active;
40306         this.setActiveClass(active);
40307         
40308         if(!active){
40309             this.fireEvent("deactivate", this);
40310             return;
40311         }
40312         
40313         this.fireEvent("activate", this);
40314         // not sure if this should happen before or after..
40315         if (!this.layout) {
40316             return; // should not happen..
40317         }
40318         var reg = false;
40319         for (var r in this.layout.regions) {
40320             reg = this.layout.getRegion(r);
40321             if (reg.getActivePanel()) {
40322                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40323                 reg.setActivePanel(reg.getActivePanel());
40324                 continue;
40325             }
40326             if (!reg.panels.length) {
40327                 continue;
40328             }
40329             reg.showPanel(reg.getPanel(0));
40330         }
40331         
40332         
40333         
40334         
40335     },
40336     
40337     /**
40338      * Returns the nested BorderLayout for this panel
40339      * @return {Roo.BorderLayout} 
40340      */
40341     getLayout : function(){
40342         return this.layout;
40343     },
40344     
40345      /**
40346      * Adds a xtype elements to the layout of the nested panel
40347      * <pre><code>
40348
40349 panel.addxtype({
40350        xtype : 'ContentPanel',
40351        region: 'west',
40352        items: [ .... ]
40353    }
40354 );
40355
40356 panel.addxtype({
40357         xtype : 'NestedLayoutPanel',
40358         region: 'west',
40359         layout: {
40360            center: { },
40361            west: { }   
40362         },
40363         items : [ ... list of content panels or nested layout panels.. ]
40364    }
40365 );
40366 </code></pre>
40367      * @param {Object} cfg Xtype definition of item to add.
40368      */
40369     addxtype : function(cfg) {
40370         return this.layout.addxtype(cfg);
40371     
40372     }
40373 });/*
40374  * Based on:
40375  * Ext JS Library 1.1.1
40376  * Copyright(c) 2006-2007, Ext JS, LLC.
40377  *
40378  * Originally Released Under LGPL - original licence link has changed is not relivant.
40379  *
40380  * Fork - LGPL
40381  * <script type="text/javascript">
40382  */
40383 /**
40384  * @class Roo.TabPanel
40385  * @extends Roo.util.Observable
40386  * A lightweight tab container.
40387  * <br><br>
40388  * Usage:
40389  * <pre><code>
40390 // basic tabs 1, built from existing content
40391 var tabs = new Roo.TabPanel("tabs1");
40392 tabs.addTab("script", "View Script");
40393 tabs.addTab("markup", "View Markup");
40394 tabs.activate("script");
40395
40396 // more advanced tabs, built from javascript
40397 var jtabs = new Roo.TabPanel("jtabs");
40398 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40399
40400 // set up the UpdateManager
40401 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40402 var updater = tab2.getUpdateManager();
40403 updater.setDefaultUrl("ajax1.htm");
40404 tab2.on('activate', updater.refresh, updater, true);
40405
40406 // Use setUrl for Ajax loading
40407 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40408 tab3.setUrl("ajax2.htm", null, true);
40409
40410 // Disabled tab
40411 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40412 tab4.disable();
40413
40414 jtabs.activate("jtabs-1");
40415  * </code></pre>
40416  * @constructor
40417  * Create a new TabPanel.
40418  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40419  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40420  */
40421 Roo.bootstrap.panel.Tabs = function(config){
40422     /**
40423     * The container element for this TabPanel.
40424     * @type Roo.Element
40425     */
40426     this.el = Roo.get(config.el);
40427     delete config.el;
40428     if(config){
40429         if(typeof config == "boolean"){
40430             this.tabPosition = config ? "bottom" : "top";
40431         }else{
40432             Roo.apply(this, config);
40433         }
40434     }
40435     
40436     if(this.tabPosition == "bottom"){
40437         // if tabs are at the bottom = create the body first.
40438         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40439         this.el.addClass("roo-tabs-bottom");
40440     }
40441     // next create the tabs holders
40442     
40443     if (this.tabPosition == "west"){
40444         
40445         var reg = this.region; // fake it..
40446         while (reg) {
40447             if (!reg.mgr.parent) {
40448                 break;
40449             }
40450             reg = reg.mgr.parent.region;
40451         }
40452         Roo.log("got nest?");
40453         Roo.log(reg);
40454         if (reg.mgr.getRegion('west')) {
40455             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40456             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40457             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40458             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40459             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40460         
40461             
40462         }
40463         
40464         
40465     } else {
40466      
40467         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40468         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40469         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40470         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40471     }
40472     
40473     
40474     if(Roo.isIE){
40475         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40476     }
40477     
40478     // finally - if tabs are at the top, then create the body last..
40479     if(this.tabPosition != "bottom"){
40480         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40481          * @type Roo.Element
40482          */
40483         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40484         this.el.addClass("roo-tabs-top");
40485     }
40486     this.items = [];
40487
40488     this.bodyEl.setStyle("position", "relative");
40489
40490     this.active = null;
40491     this.activateDelegate = this.activate.createDelegate(this);
40492
40493     this.addEvents({
40494         /**
40495          * @event tabchange
40496          * Fires when the active tab changes
40497          * @param {Roo.TabPanel} this
40498          * @param {Roo.TabPanelItem} activePanel The new active tab
40499          */
40500         "tabchange": true,
40501         /**
40502          * @event beforetabchange
40503          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40504          * @param {Roo.TabPanel} this
40505          * @param {Object} e Set cancel to true on this object to cancel the tab change
40506          * @param {Roo.TabPanelItem} tab The tab being changed to
40507          */
40508         "beforetabchange" : true
40509     });
40510
40511     Roo.EventManager.onWindowResize(this.onResize, this);
40512     this.cpad = this.el.getPadding("lr");
40513     this.hiddenCount = 0;
40514
40515
40516     // toolbar on the tabbar support...
40517     if (this.toolbar) {
40518         alert("no toolbar support yet");
40519         this.toolbar  = false;
40520         /*
40521         var tcfg = this.toolbar;
40522         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40523         this.toolbar = new Roo.Toolbar(tcfg);
40524         if (Roo.isSafari) {
40525             var tbl = tcfg.container.child('table', true);
40526             tbl.setAttribute('width', '100%');
40527         }
40528         */
40529         
40530     }
40531    
40532
40533
40534     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40535 };
40536
40537 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40538     /*
40539      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40540      */
40541     tabPosition : "top",
40542     /*
40543      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40544      */
40545     currentTabWidth : 0,
40546     /*
40547      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40548      */
40549     minTabWidth : 40,
40550     /*
40551      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40552      */
40553     maxTabWidth : 250,
40554     /*
40555      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40556      */
40557     preferredTabWidth : 175,
40558     /*
40559      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40560      */
40561     resizeTabs : false,
40562     /*
40563      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40564      */
40565     monitorResize : true,
40566     /*
40567      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40568      */
40569     toolbar : false,  // set by caller..
40570     
40571     region : false, /// set by caller
40572     
40573     disableTooltips : true, // not used yet...
40574
40575     /**
40576      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40577      * @param {String} id The id of the div to use <b>or create</b>
40578      * @param {String} text The text for the tab
40579      * @param {String} content (optional) Content to put in the TabPanelItem body
40580      * @param {Boolean} closable (optional) True to create a close icon on the tab
40581      * @return {Roo.TabPanelItem} The created TabPanelItem
40582      */
40583     addTab : function(id, text, content, closable, tpl)
40584     {
40585         var item = new Roo.bootstrap.panel.TabItem({
40586             panel: this,
40587             id : id,
40588             text : text,
40589             closable : closable,
40590             tpl : tpl
40591         });
40592         this.addTabItem(item);
40593         if(content){
40594             item.setContent(content);
40595         }
40596         return item;
40597     },
40598
40599     /**
40600      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40601      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40602      * @return {Roo.TabPanelItem}
40603      */
40604     getTab : function(id){
40605         return this.items[id];
40606     },
40607
40608     /**
40609      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40610      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40611      */
40612     hideTab : function(id){
40613         var t = this.items[id];
40614         if(!t.isHidden()){
40615            t.setHidden(true);
40616            this.hiddenCount++;
40617            this.autoSizeTabs();
40618         }
40619     },
40620
40621     /**
40622      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40623      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40624      */
40625     unhideTab : function(id){
40626         var t = this.items[id];
40627         if(t.isHidden()){
40628            t.setHidden(false);
40629            this.hiddenCount--;
40630            this.autoSizeTabs();
40631         }
40632     },
40633
40634     /**
40635      * Adds an existing {@link Roo.TabPanelItem}.
40636      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40637      */
40638     addTabItem : function(item)
40639     {
40640         this.items[item.id] = item;
40641         this.items.push(item);
40642         this.autoSizeTabs();
40643       //  if(this.resizeTabs){
40644     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40645   //         this.autoSizeTabs();
40646 //        }else{
40647 //            item.autoSize();
40648        // }
40649     },
40650
40651     /**
40652      * Removes a {@link Roo.TabPanelItem}.
40653      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40654      */
40655     removeTab : function(id){
40656         var items = this.items;
40657         var tab = items[id];
40658         if(!tab) { return; }
40659         var index = items.indexOf(tab);
40660         if(this.active == tab && items.length > 1){
40661             var newTab = this.getNextAvailable(index);
40662             if(newTab) {
40663                 newTab.activate();
40664             }
40665         }
40666         this.stripEl.dom.removeChild(tab.pnode.dom);
40667         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40668             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40669         }
40670         items.splice(index, 1);
40671         delete this.items[tab.id];
40672         tab.fireEvent("close", tab);
40673         tab.purgeListeners();
40674         this.autoSizeTabs();
40675     },
40676
40677     getNextAvailable : function(start){
40678         var items = this.items;
40679         var index = start;
40680         // look for a next tab that will slide over to
40681         // replace the one being removed
40682         while(index < items.length){
40683             var item = items[++index];
40684             if(item && !item.isHidden()){
40685                 return item;
40686             }
40687         }
40688         // if one isn't found select the previous tab (on the left)
40689         index = start;
40690         while(index >= 0){
40691             var item = items[--index];
40692             if(item && !item.isHidden()){
40693                 return item;
40694             }
40695         }
40696         return null;
40697     },
40698
40699     /**
40700      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40701      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40702      */
40703     disableTab : function(id){
40704         var tab = this.items[id];
40705         if(tab && this.active != tab){
40706             tab.disable();
40707         }
40708     },
40709
40710     /**
40711      * Enables a {@link Roo.TabPanelItem} that is disabled.
40712      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40713      */
40714     enableTab : function(id){
40715         var tab = this.items[id];
40716         tab.enable();
40717     },
40718
40719     /**
40720      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40721      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40722      * @return {Roo.TabPanelItem} The TabPanelItem.
40723      */
40724     activate : function(id)
40725     {
40726         //Roo.log('activite:'  + id);
40727         
40728         var tab = this.items[id];
40729         if(!tab){
40730             return null;
40731         }
40732         if(tab == this.active || tab.disabled){
40733             return tab;
40734         }
40735         var e = {};
40736         this.fireEvent("beforetabchange", this, e, tab);
40737         if(e.cancel !== true && !tab.disabled){
40738             if(this.active){
40739                 this.active.hide();
40740             }
40741             this.active = this.items[id];
40742             this.active.show();
40743             this.fireEvent("tabchange", this, this.active);
40744         }
40745         return tab;
40746     },
40747
40748     /**
40749      * Gets the active {@link Roo.TabPanelItem}.
40750      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40751      */
40752     getActiveTab : function(){
40753         return this.active;
40754     },
40755
40756     /**
40757      * Updates the tab body element to fit the height of the container element
40758      * for overflow scrolling
40759      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40760      */
40761     syncHeight : function(targetHeight){
40762         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40763         var bm = this.bodyEl.getMargins();
40764         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40765         this.bodyEl.setHeight(newHeight);
40766         return newHeight;
40767     },
40768
40769     onResize : function(){
40770         if(this.monitorResize){
40771             this.autoSizeTabs();
40772         }
40773     },
40774
40775     /**
40776      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40777      */
40778     beginUpdate : function(){
40779         this.updating = true;
40780     },
40781
40782     /**
40783      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40784      */
40785     endUpdate : function(){
40786         this.updating = false;
40787         this.autoSizeTabs();
40788     },
40789
40790     /**
40791      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40792      */
40793     autoSizeTabs : function()
40794     {
40795         var count = this.items.length;
40796         var vcount = count - this.hiddenCount;
40797         
40798         if (vcount < 2) {
40799             this.stripEl.hide();
40800         } else {
40801             this.stripEl.show();
40802         }
40803         
40804         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40805             return;
40806         }
40807         
40808         
40809         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40810         var availWidth = Math.floor(w / vcount);
40811         var b = this.stripBody;
40812         if(b.getWidth() > w){
40813             var tabs = this.items;
40814             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40815             if(availWidth < this.minTabWidth){
40816                 /*if(!this.sleft){    // incomplete scrolling code
40817                     this.createScrollButtons();
40818                 }
40819                 this.showScroll();
40820                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40821             }
40822         }else{
40823             if(this.currentTabWidth < this.preferredTabWidth){
40824                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40825             }
40826         }
40827     },
40828
40829     /**
40830      * Returns the number of tabs in this TabPanel.
40831      * @return {Number}
40832      */
40833      getCount : function(){
40834          return this.items.length;
40835      },
40836
40837     /**
40838      * Resizes all the tabs to the passed width
40839      * @param {Number} The new width
40840      */
40841     setTabWidth : function(width){
40842         this.currentTabWidth = width;
40843         for(var i = 0, len = this.items.length; i < len; i++) {
40844                 if(!this.items[i].isHidden()) {
40845                 this.items[i].setWidth(width);
40846             }
40847         }
40848     },
40849
40850     /**
40851      * Destroys this TabPanel
40852      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40853      */
40854     destroy : function(removeEl){
40855         Roo.EventManager.removeResizeListener(this.onResize, this);
40856         for(var i = 0, len = this.items.length; i < len; i++){
40857             this.items[i].purgeListeners();
40858         }
40859         if(removeEl === true){
40860             this.el.update("");
40861             this.el.remove();
40862         }
40863     },
40864     
40865     createStrip : function(container)
40866     {
40867         var strip = document.createElement("nav");
40868         strip.className = Roo.bootstrap.version == 4 ?
40869             "navbar-light bg-light" : 
40870             "navbar navbar-default"; //"x-tabs-wrap";
40871         container.appendChild(strip);
40872         return strip;
40873     },
40874     
40875     createStripList : function(strip)
40876     {
40877         // div wrapper for retard IE
40878         // returns the "tr" element.
40879         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40880         //'<div class="x-tabs-strip-wrap">'+
40881           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40882           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40883         return strip.firstChild; //.firstChild.firstChild.firstChild;
40884     },
40885     createBody : function(container)
40886     {
40887         var body = document.createElement("div");
40888         Roo.id(body, "tab-body");
40889         //Roo.fly(body).addClass("x-tabs-body");
40890         Roo.fly(body).addClass("tab-content");
40891         container.appendChild(body);
40892         return body;
40893     },
40894     createItemBody :function(bodyEl, id){
40895         var body = Roo.getDom(id);
40896         if(!body){
40897             body = document.createElement("div");
40898             body.id = id;
40899         }
40900         //Roo.fly(body).addClass("x-tabs-item-body");
40901         Roo.fly(body).addClass("tab-pane");
40902          bodyEl.insertBefore(body, bodyEl.firstChild);
40903         return body;
40904     },
40905     /** @private */
40906     createStripElements :  function(stripEl, text, closable, tpl)
40907     {
40908         var td = document.createElement("li"); // was td..
40909         td.className = 'nav-item';
40910         
40911         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40912         
40913         
40914         stripEl.appendChild(td);
40915         /*if(closable){
40916             td.className = "x-tabs-closable";
40917             if(!this.closeTpl){
40918                 this.closeTpl = new Roo.Template(
40919                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40920                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40921                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40922                 );
40923             }
40924             var el = this.closeTpl.overwrite(td, {"text": text});
40925             var close = el.getElementsByTagName("div")[0];
40926             var inner = el.getElementsByTagName("em")[0];
40927             return {"el": el, "close": close, "inner": inner};
40928         } else {
40929         */
40930         // not sure what this is..
40931 //            if(!this.tabTpl){
40932                 //this.tabTpl = new Roo.Template(
40933                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40934                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40935                 //);
40936 //                this.tabTpl = new Roo.Template(
40937 //                   '<a href="#">' +
40938 //                   '<span unselectable="on"' +
40939 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40940 //                            ' >{text}</span></a>'
40941 //                );
40942 //                
40943 //            }
40944
40945
40946             var template = tpl || this.tabTpl || false;
40947             
40948             if(!template){
40949                 template =  new Roo.Template(
40950                         Roo.bootstrap.version == 4 ? 
40951                             (
40952                                 '<a class="nav-link" href="#" unselectable="on"' +
40953                                      (this.disableTooltips ? '' : ' title="{text}"') +
40954                                      ' >{text}</a>'
40955                             ) : (
40956                                 '<a class="nav-link" href="#">' +
40957                                 '<span unselectable="on"' +
40958                                          (this.disableTooltips ? '' : ' title="{text}"') +
40959                                     ' >{text}</span></a>'
40960                             )
40961                 );
40962             }
40963             
40964             switch (typeof(template)) {
40965                 case 'object' :
40966                     break;
40967                 case 'string' :
40968                     template = new Roo.Template(template);
40969                     break;
40970                 default :
40971                     break;
40972             }
40973             
40974             var el = template.overwrite(td, {"text": text});
40975             
40976             var inner = el.getElementsByTagName("span")[0];
40977             
40978             return {"el": el, "inner": inner};
40979             
40980     }
40981         
40982     
40983 });
40984
40985 /**
40986  * @class Roo.TabPanelItem
40987  * @extends Roo.util.Observable
40988  * Represents an individual item (tab plus body) in a TabPanel.
40989  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40990  * @param {String} id The id of this TabPanelItem
40991  * @param {String} text The text for the tab of this TabPanelItem
40992  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40993  */
40994 Roo.bootstrap.panel.TabItem = function(config){
40995     /**
40996      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40997      * @type Roo.TabPanel
40998      */
40999     this.tabPanel = config.panel;
41000     /**
41001      * The id for this TabPanelItem
41002      * @type String
41003      */
41004     this.id = config.id;
41005     /** @private */
41006     this.disabled = false;
41007     /** @private */
41008     this.text = config.text;
41009     /** @private */
41010     this.loaded = false;
41011     this.closable = config.closable;
41012
41013     /**
41014      * The body element for this TabPanelItem.
41015      * @type Roo.Element
41016      */
41017     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41018     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41019     this.bodyEl.setStyle("display", "block");
41020     this.bodyEl.setStyle("zoom", "1");
41021     //this.hideAction();
41022
41023     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41024     /** @private */
41025     this.el = Roo.get(els.el);
41026     this.inner = Roo.get(els.inner, true);
41027      this.textEl = Roo.bootstrap.version == 4 ?
41028         this.el : Roo.get(this.el.dom.firstChild, true);
41029
41030     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41031     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41032
41033     
41034 //    this.el.on("mousedown", this.onTabMouseDown, this);
41035     this.el.on("click", this.onTabClick, this);
41036     /** @private */
41037     if(config.closable){
41038         var c = Roo.get(els.close, true);
41039         c.dom.title = this.closeText;
41040         c.addClassOnOver("close-over");
41041         c.on("click", this.closeClick, this);
41042      }
41043
41044     this.addEvents({
41045          /**
41046          * @event activate
41047          * Fires when this tab becomes the active tab.
41048          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41049          * @param {Roo.TabPanelItem} this
41050          */
41051         "activate": true,
41052         /**
41053          * @event beforeclose
41054          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41055          * @param {Roo.TabPanelItem} this
41056          * @param {Object} e Set cancel to true on this object to cancel the close.
41057          */
41058         "beforeclose": true,
41059         /**
41060          * @event close
41061          * Fires when this tab is closed.
41062          * @param {Roo.TabPanelItem} this
41063          */
41064          "close": true,
41065         /**
41066          * @event deactivate
41067          * Fires when this tab is no longer the active tab.
41068          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41069          * @param {Roo.TabPanelItem} this
41070          */
41071          "deactivate" : true
41072     });
41073     this.hidden = false;
41074
41075     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41076 };
41077
41078 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41079            {
41080     purgeListeners : function(){
41081        Roo.util.Observable.prototype.purgeListeners.call(this);
41082        this.el.removeAllListeners();
41083     },
41084     /**
41085      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41086      */
41087     show : function(){
41088         this.status_node.addClass("active");
41089         this.showAction();
41090         if(Roo.isOpera){
41091             this.tabPanel.stripWrap.repaint();
41092         }
41093         this.fireEvent("activate", this.tabPanel, this);
41094     },
41095
41096     /**
41097      * Returns true if this tab is the active tab.
41098      * @return {Boolean}
41099      */
41100     isActive : function(){
41101         return this.tabPanel.getActiveTab() == this;
41102     },
41103
41104     /**
41105      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41106      */
41107     hide : function(){
41108         this.status_node.removeClass("active");
41109         this.hideAction();
41110         this.fireEvent("deactivate", this.tabPanel, this);
41111     },
41112
41113     hideAction : function(){
41114         this.bodyEl.hide();
41115         this.bodyEl.setStyle("position", "absolute");
41116         this.bodyEl.setLeft("-20000px");
41117         this.bodyEl.setTop("-20000px");
41118     },
41119
41120     showAction : function(){
41121         this.bodyEl.setStyle("position", "relative");
41122         this.bodyEl.setTop("");
41123         this.bodyEl.setLeft("");
41124         this.bodyEl.show();
41125     },
41126
41127     /**
41128      * Set the tooltip for the tab.
41129      * @param {String} tooltip The tab's tooltip
41130      */
41131     setTooltip : function(text){
41132         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41133             this.textEl.dom.qtip = text;
41134             this.textEl.dom.removeAttribute('title');
41135         }else{
41136             this.textEl.dom.title = text;
41137         }
41138     },
41139
41140     onTabClick : function(e){
41141         e.preventDefault();
41142         this.tabPanel.activate(this.id);
41143     },
41144
41145     onTabMouseDown : function(e){
41146         e.preventDefault();
41147         this.tabPanel.activate(this.id);
41148     },
41149 /*
41150     getWidth : function(){
41151         return this.inner.getWidth();
41152     },
41153
41154     setWidth : function(width){
41155         var iwidth = width - this.linode.getPadding("lr");
41156         this.inner.setWidth(iwidth);
41157         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41158         this.linode.setWidth(width);
41159     },
41160 */
41161     /**
41162      * Show or hide the tab
41163      * @param {Boolean} hidden True to hide or false to show.
41164      */
41165     setHidden : function(hidden){
41166         this.hidden = hidden;
41167         this.linode.setStyle("display", hidden ? "none" : "");
41168     },
41169
41170     /**
41171      * Returns true if this tab is "hidden"
41172      * @return {Boolean}
41173      */
41174     isHidden : function(){
41175         return this.hidden;
41176     },
41177
41178     /**
41179      * Returns the text for this tab
41180      * @return {String}
41181      */
41182     getText : function(){
41183         return this.text;
41184     },
41185     /*
41186     autoSize : function(){
41187         //this.el.beginMeasure();
41188         this.textEl.setWidth(1);
41189         /*
41190          *  #2804 [new] Tabs in Roojs
41191          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41192          */
41193         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41194         //this.el.endMeasure();
41195     //},
41196
41197     /**
41198      * Sets the text for the tab (Note: this also sets the tooltip text)
41199      * @param {String} text The tab's text and tooltip
41200      */
41201     setText : function(text){
41202         this.text = text;
41203         this.textEl.update(text);
41204         this.setTooltip(text);
41205         //if(!this.tabPanel.resizeTabs){
41206         //    this.autoSize();
41207         //}
41208     },
41209     /**
41210      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41211      */
41212     activate : function(){
41213         this.tabPanel.activate(this.id);
41214     },
41215
41216     /**
41217      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41218      */
41219     disable : function(){
41220         if(this.tabPanel.active != this){
41221             this.disabled = true;
41222             this.status_node.addClass("disabled");
41223         }
41224     },
41225
41226     /**
41227      * Enables this TabPanelItem if it was previously disabled.
41228      */
41229     enable : function(){
41230         this.disabled = false;
41231         this.status_node.removeClass("disabled");
41232     },
41233
41234     /**
41235      * Sets the content for this TabPanelItem.
41236      * @param {String} content The content
41237      * @param {Boolean} loadScripts true to look for and load scripts
41238      */
41239     setContent : function(content, loadScripts){
41240         this.bodyEl.update(content, loadScripts);
41241     },
41242
41243     /**
41244      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41245      * @return {Roo.UpdateManager} The UpdateManager
41246      */
41247     getUpdateManager : function(){
41248         return this.bodyEl.getUpdateManager();
41249     },
41250
41251     /**
41252      * Set a URL to be used to load the content for this TabPanelItem.
41253      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41254      * @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)
41255      * @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)
41256      * @return {Roo.UpdateManager} The UpdateManager
41257      */
41258     setUrl : function(url, params, loadOnce){
41259         if(this.refreshDelegate){
41260             this.un('activate', this.refreshDelegate);
41261         }
41262         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41263         this.on("activate", this.refreshDelegate);
41264         return this.bodyEl.getUpdateManager();
41265     },
41266
41267     /** @private */
41268     _handleRefresh : function(url, params, loadOnce){
41269         if(!loadOnce || !this.loaded){
41270             var updater = this.bodyEl.getUpdateManager();
41271             updater.update(url, params, this._setLoaded.createDelegate(this));
41272         }
41273     },
41274
41275     /**
41276      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41277      *   Will fail silently if the setUrl method has not been called.
41278      *   This does not activate the panel, just updates its content.
41279      */
41280     refresh : function(){
41281         if(this.refreshDelegate){
41282            this.loaded = false;
41283            this.refreshDelegate();
41284         }
41285     },
41286
41287     /** @private */
41288     _setLoaded : function(){
41289         this.loaded = true;
41290     },
41291
41292     /** @private */
41293     closeClick : function(e){
41294         var o = {};
41295         e.stopEvent();
41296         this.fireEvent("beforeclose", this, o);
41297         if(o.cancel !== true){
41298             this.tabPanel.removeTab(this.id);
41299         }
41300     },
41301     /**
41302      * The text displayed in the tooltip for the close icon.
41303      * @type String
41304      */
41305     closeText : "Close this tab"
41306 });
41307 /**
41308 *    This script refer to:
41309 *    Title: International Telephone Input
41310 *    Author: Jack O'Connor
41311 *    Code version:  v12.1.12
41312 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41313 **/
41314
41315 Roo.bootstrap.PhoneInputData = function() {
41316     var d = [
41317       [
41318         "Afghanistan (‫افغانستان‬‎)",
41319         "af",
41320         "93"
41321       ],
41322       [
41323         "Albania (Shqipëri)",
41324         "al",
41325         "355"
41326       ],
41327       [
41328         "Algeria (‫الجزائر‬‎)",
41329         "dz",
41330         "213"
41331       ],
41332       [
41333         "American Samoa",
41334         "as",
41335         "1684"
41336       ],
41337       [
41338         "Andorra",
41339         "ad",
41340         "376"
41341       ],
41342       [
41343         "Angola",
41344         "ao",
41345         "244"
41346       ],
41347       [
41348         "Anguilla",
41349         "ai",
41350         "1264"
41351       ],
41352       [
41353         "Antigua and Barbuda",
41354         "ag",
41355         "1268"
41356       ],
41357       [
41358         "Argentina",
41359         "ar",
41360         "54"
41361       ],
41362       [
41363         "Armenia (Հայաստան)",
41364         "am",
41365         "374"
41366       ],
41367       [
41368         "Aruba",
41369         "aw",
41370         "297"
41371       ],
41372       [
41373         "Australia",
41374         "au",
41375         "61",
41376         0
41377       ],
41378       [
41379         "Austria (Österreich)",
41380         "at",
41381         "43"
41382       ],
41383       [
41384         "Azerbaijan (Azərbaycan)",
41385         "az",
41386         "994"
41387       ],
41388       [
41389         "Bahamas",
41390         "bs",
41391         "1242"
41392       ],
41393       [
41394         "Bahrain (‫البحرين‬‎)",
41395         "bh",
41396         "973"
41397       ],
41398       [
41399         "Bangladesh (বাংলাদেশ)",
41400         "bd",
41401         "880"
41402       ],
41403       [
41404         "Barbados",
41405         "bb",
41406         "1246"
41407       ],
41408       [
41409         "Belarus (Беларусь)",
41410         "by",
41411         "375"
41412       ],
41413       [
41414         "Belgium (België)",
41415         "be",
41416         "32"
41417       ],
41418       [
41419         "Belize",
41420         "bz",
41421         "501"
41422       ],
41423       [
41424         "Benin (Bénin)",
41425         "bj",
41426         "229"
41427       ],
41428       [
41429         "Bermuda",
41430         "bm",
41431         "1441"
41432       ],
41433       [
41434         "Bhutan (འབྲུག)",
41435         "bt",
41436         "975"
41437       ],
41438       [
41439         "Bolivia",
41440         "bo",
41441         "591"
41442       ],
41443       [
41444         "Bosnia and Herzegovina (Босна и Херцеговина)",
41445         "ba",
41446         "387"
41447       ],
41448       [
41449         "Botswana",
41450         "bw",
41451         "267"
41452       ],
41453       [
41454         "Brazil (Brasil)",
41455         "br",
41456         "55"
41457       ],
41458       [
41459         "British Indian Ocean Territory",
41460         "io",
41461         "246"
41462       ],
41463       [
41464         "British Virgin Islands",
41465         "vg",
41466         "1284"
41467       ],
41468       [
41469         "Brunei",
41470         "bn",
41471         "673"
41472       ],
41473       [
41474         "Bulgaria (България)",
41475         "bg",
41476         "359"
41477       ],
41478       [
41479         "Burkina Faso",
41480         "bf",
41481         "226"
41482       ],
41483       [
41484         "Burundi (Uburundi)",
41485         "bi",
41486         "257"
41487       ],
41488       [
41489         "Cambodia (កម្ពុជា)",
41490         "kh",
41491         "855"
41492       ],
41493       [
41494         "Cameroon (Cameroun)",
41495         "cm",
41496         "237"
41497       ],
41498       [
41499         "Canada",
41500         "ca",
41501         "1",
41502         1,
41503         ["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"]
41504       ],
41505       [
41506         "Cape Verde (Kabu Verdi)",
41507         "cv",
41508         "238"
41509       ],
41510       [
41511         "Caribbean Netherlands",
41512         "bq",
41513         "599",
41514         1
41515       ],
41516       [
41517         "Cayman Islands",
41518         "ky",
41519         "1345"
41520       ],
41521       [
41522         "Central African Republic (République centrafricaine)",
41523         "cf",
41524         "236"
41525       ],
41526       [
41527         "Chad (Tchad)",
41528         "td",
41529         "235"
41530       ],
41531       [
41532         "Chile",
41533         "cl",
41534         "56"
41535       ],
41536       [
41537         "China (中国)",
41538         "cn",
41539         "86"
41540       ],
41541       [
41542         "Christmas Island",
41543         "cx",
41544         "61",
41545         2
41546       ],
41547       [
41548         "Cocos (Keeling) Islands",
41549         "cc",
41550         "61",
41551         1
41552       ],
41553       [
41554         "Colombia",
41555         "co",
41556         "57"
41557       ],
41558       [
41559         "Comoros (‫جزر القمر‬‎)",
41560         "km",
41561         "269"
41562       ],
41563       [
41564         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41565         "cd",
41566         "243"
41567       ],
41568       [
41569         "Congo (Republic) (Congo-Brazzaville)",
41570         "cg",
41571         "242"
41572       ],
41573       [
41574         "Cook Islands",
41575         "ck",
41576         "682"
41577       ],
41578       [
41579         "Costa Rica",
41580         "cr",
41581         "506"
41582       ],
41583       [
41584         "Côte d’Ivoire",
41585         "ci",
41586         "225"
41587       ],
41588       [
41589         "Croatia (Hrvatska)",
41590         "hr",
41591         "385"
41592       ],
41593       [
41594         "Cuba",
41595         "cu",
41596         "53"
41597       ],
41598       [
41599         "Curaçao",
41600         "cw",
41601         "599",
41602         0
41603       ],
41604       [
41605         "Cyprus (Κύπρος)",
41606         "cy",
41607         "357"
41608       ],
41609       [
41610         "Czech Republic (Česká republika)",
41611         "cz",
41612         "420"
41613       ],
41614       [
41615         "Denmark (Danmark)",
41616         "dk",
41617         "45"
41618       ],
41619       [
41620         "Djibouti",
41621         "dj",
41622         "253"
41623       ],
41624       [
41625         "Dominica",
41626         "dm",
41627         "1767"
41628       ],
41629       [
41630         "Dominican Republic (República Dominicana)",
41631         "do",
41632         "1",
41633         2,
41634         ["809", "829", "849"]
41635       ],
41636       [
41637         "Ecuador",
41638         "ec",
41639         "593"
41640       ],
41641       [
41642         "Egypt (‫مصر‬‎)",
41643         "eg",
41644         "20"
41645       ],
41646       [
41647         "El Salvador",
41648         "sv",
41649         "503"
41650       ],
41651       [
41652         "Equatorial Guinea (Guinea Ecuatorial)",
41653         "gq",
41654         "240"
41655       ],
41656       [
41657         "Eritrea",
41658         "er",
41659         "291"
41660       ],
41661       [
41662         "Estonia (Eesti)",
41663         "ee",
41664         "372"
41665       ],
41666       [
41667         "Ethiopia",
41668         "et",
41669         "251"
41670       ],
41671       [
41672         "Falkland Islands (Islas Malvinas)",
41673         "fk",
41674         "500"
41675       ],
41676       [
41677         "Faroe Islands (Føroyar)",
41678         "fo",
41679         "298"
41680       ],
41681       [
41682         "Fiji",
41683         "fj",
41684         "679"
41685       ],
41686       [
41687         "Finland (Suomi)",
41688         "fi",
41689         "358",
41690         0
41691       ],
41692       [
41693         "France",
41694         "fr",
41695         "33"
41696       ],
41697       [
41698         "French Guiana (Guyane française)",
41699         "gf",
41700         "594"
41701       ],
41702       [
41703         "French Polynesia (Polynésie française)",
41704         "pf",
41705         "689"
41706       ],
41707       [
41708         "Gabon",
41709         "ga",
41710         "241"
41711       ],
41712       [
41713         "Gambia",
41714         "gm",
41715         "220"
41716       ],
41717       [
41718         "Georgia (საქართველო)",
41719         "ge",
41720         "995"
41721       ],
41722       [
41723         "Germany (Deutschland)",
41724         "de",
41725         "49"
41726       ],
41727       [
41728         "Ghana (Gaana)",
41729         "gh",
41730         "233"
41731       ],
41732       [
41733         "Gibraltar",
41734         "gi",
41735         "350"
41736       ],
41737       [
41738         "Greece (Ελλάδα)",
41739         "gr",
41740         "30"
41741       ],
41742       [
41743         "Greenland (Kalaallit Nunaat)",
41744         "gl",
41745         "299"
41746       ],
41747       [
41748         "Grenada",
41749         "gd",
41750         "1473"
41751       ],
41752       [
41753         "Guadeloupe",
41754         "gp",
41755         "590",
41756         0
41757       ],
41758       [
41759         "Guam",
41760         "gu",
41761         "1671"
41762       ],
41763       [
41764         "Guatemala",
41765         "gt",
41766         "502"
41767       ],
41768       [
41769         "Guernsey",
41770         "gg",
41771         "44",
41772         1
41773       ],
41774       [
41775         "Guinea (Guinée)",
41776         "gn",
41777         "224"
41778       ],
41779       [
41780         "Guinea-Bissau (Guiné Bissau)",
41781         "gw",
41782         "245"
41783       ],
41784       [
41785         "Guyana",
41786         "gy",
41787         "592"
41788       ],
41789       [
41790         "Haiti",
41791         "ht",
41792         "509"
41793       ],
41794       [
41795         "Honduras",
41796         "hn",
41797         "504"
41798       ],
41799       [
41800         "Hong Kong (香港)",
41801         "hk",
41802         "852"
41803       ],
41804       [
41805         "Hungary (Magyarország)",
41806         "hu",
41807         "36"
41808       ],
41809       [
41810         "Iceland (Ísland)",
41811         "is",
41812         "354"
41813       ],
41814       [
41815         "India (भारत)",
41816         "in",
41817         "91"
41818       ],
41819       [
41820         "Indonesia",
41821         "id",
41822         "62"
41823       ],
41824       [
41825         "Iran (‫ایران‬‎)",
41826         "ir",
41827         "98"
41828       ],
41829       [
41830         "Iraq (‫العراق‬‎)",
41831         "iq",
41832         "964"
41833       ],
41834       [
41835         "Ireland",
41836         "ie",
41837         "353"
41838       ],
41839       [
41840         "Isle of Man",
41841         "im",
41842         "44",
41843         2
41844       ],
41845       [
41846         "Israel (‫ישראל‬‎)",
41847         "il",
41848         "972"
41849       ],
41850       [
41851         "Italy (Italia)",
41852         "it",
41853         "39",
41854         0
41855       ],
41856       [
41857         "Jamaica",
41858         "jm",
41859         "1876"
41860       ],
41861       [
41862         "Japan (日本)",
41863         "jp",
41864         "81"
41865       ],
41866       [
41867         "Jersey",
41868         "je",
41869         "44",
41870         3
41871       ],
41872       [
41873         "Jordan (‫الأردن‬‎)",
41874         "jo",
41875         "962"
41876       ],
41877       [
41878         "Kazakhstan (Казахстан)",
41879         "kz",
41880         "7",
41881         1
41882       ],
41883       [
41884         "Kenya",
41885         "ke",
41886         "254"
41887       ],
41888       [
41889         "Kiribati",
41890         "ki",
41891         "686"
41892       ],
41893       [
41894         "Kosovo",
41895         "xk",
41896         "383"
41897       ],
41898       [
41899         "Kuwait (‫الكويت‬‎)",
41900         "kw",
41901         "965"
41902       ],
41903       [
41904         "Kyrgyzstan (Кыргызстан)",
41905         "kg",
41906         "996"
41907       ],
41908       [
41909         "Laos (ລາວ)",
41910         "la",
41911         "856"
41912       ],
41913       [
41914         "Latvia (Latvija)",
41915         "lv",
41916         "371"
41917       ],
41918       [
41919         "Lebanon (‫لبنان‬‎)",
41920         "lb",
41921         "961"
41922       ],
41923       [
41924         "Lesotho",
41925         "ls",
41926         "266"
41927       ],
41928       [
41929         "Liberia",
41930         "lr",
41931         "231"
41932       ],
41933       [
41934         "Libya (‫ليبيا‬‎)",
41935         "ly",
41936         "218"
41937       ],
41938       [
41939         "Liechtenstein",
41940         "li",
41941         "423"
41942       ],
41943       [
41944         "Lithuania (Lietuva)",
41945         "lt",
41946         "370"
41947       ],
41948       [
41949         "Luxembourg",
41950         "lu",
41951         "352"
41952       ],
41953       [
41954         "Macau (澳門)",
41955         "mo",
41956         "853"
41957       ],
41958       [
41959         "Macedonia (FYROM) (Македонија)",
41960         "mk",
41961         "389"
41962       ],
41963       [
41964         "Madagascar (Madagasikara)",
41965         "mg",
41966         "261"
41967       ],
41968       [
41969         "Malawi",
41970         "mw",
41971         "265"
41972       ],
41973       [
41974         "Malaysia",
41975         "my",
41976         "60"
41977       ],
41978       [
41979         "Maldives",
41980         "mv",
41981         "960"
41982       ],
41983       [
41984         "Mali",
41985         "ml",
41986         "223"
41987       ],
41988       [
41989         "Malta",
41990         "mt",
41991         "356"
41992       ],
41993       [
41994         "Marshall Islands",
41995         "mh",
41996         "692"
41997       ],
41998       [
41999         "Martinique",
42000         "mq",
42001         "596"
42002       ],
42003       [
42004         "Mauritania (‫موريتانيا‬‎)",
42005         "mr",
42006         "222"
42007       ],
42008       [
42009         "Mauritius (Moris)",
42010         "mu",
42011         "230"
42012       ],
42013       [
42014         "Mayotte",
42015         "yt",
42016         "262",
42017         1
42018       ],
42019       [
42020         "Mexico (México)",
42021         "mx",
42022         "52"
42023       ],
42024       [
42025         "Micronesia",
42026         "fm",
42027         "691"
42028       ],
42029       [
42030         "Moldova (Republica Moldova)",
42031         "md",
42032         "373"
42033       ],
42034       [
42035         "Monaco",
42036         "mc",
42037         "377"
42038       ],
42039       [
42040         "Mongolia (Монгол)",
42041         "mn",
42042         "976"
42043       ],
42044       [
42045         "Montenegro (Crna Gora)",
42046         "me",
42047         "382"
42048       ],
42049       [
42050         "Montserrat",
42051         "ms",
42052         "1664"
42053       ],
42054       [
42055         "Morocco (‫المغرب‬‎)",
42056         "ma",
42057         "212",
42058         0
42059       ],
42060       [
42061         "Mozambique (Moçambique)",
42062         "mz",
42063         "258"
42064       ],
42065       [
42066         "Myanmar (Burma) (မြန်မာ)",
42067         "mm",
42068         "95"
42069       ],
42070       [
42071         "Namibia (Namibië)",
42072         "na",
42073         "264"
42074       ],
42075       [
42076         "Nauru",
42077         "nr",
42078         "674"
42079       ],
42080       [
42081         "Nepal (नेपाल)",
42082         "np",
42083         "977"
42084       ],
42085       [
42086         "Netherlands (Nederland)",
42087         "nl",
42088         "31"
42089       ],
42090       [
42091         "New Caledonia (Nouvelle-Calédonie)",
42092         "nc",
42093         "687"
42094       ],
42095       [
42096         "New Zealand",
42097         "nz",
42098         "64"
42099       ],
42100       [
42101         "Nicaragua",
42102         "ni",
42103         "505"
42104       ],
42105       [
42106         "Niger (Nijar)",
42107         "ne",
42108         "227"
42109       ],
42110       [
42111         "Nigeria",
42112         "ng",
42113         "234"
42114       ],
42115       [
42116         "Niue",
42117         "nu",
42118         "683"
42119       ],
42120       [
42121         "Norfolk Island",
42122         "nf",
42123         "672"
42124       ],
42125       [
42126         "North Korea (조선 민주주의 인민 공화국)",
42127         "kp",
42128         "850"
42129       ],
42130       [
42131         "Northern Mariana Islands",
42132         "mp",
42133         "1670"
42134       ],
42135       [
42136         "Norway (Norge)",
42137         "no",
42138         "47",
42139         0
42140       ],
42141       [
42142         "Oman (‫عُمان‬‎)",
42143         "om",
42144         "968"
42145       ],
42146       [
42147         "Pakistan (‫پاکستان‬‎)",
42148         "pk",
42149         "92"
42150       ],
42151       [
42152         "Palau",
42153         "pw",
42154         "680"
42155       ],
42156       [
42157         "Palestine (‫فلسطين‬‎)",
42158         "ps",
42159         "970"
42160       ],
42161       [
42162         "Panama (Panamá)",
42163         "pa",
42164         "507"
42165       ],
42166       [
42167         "Papua New Guinea",
42168         "pg",
42169         "675"
42170       ],
42171       [
42172         "Paraguay",
42173         "py",
42174         "595"
42175       ],
42176       [
42177         "Peru (Perú)",
42178         "pe",
42179         "51"
42180       ],
42181       [
42182         "Philippines",
42183         "ph",
42184         "63"
42185       ],
42186       [
42187         "Poland (Polska)",
42188         "pl",
42189         "48"
42190       ],
42191       [
42192         "Portugal",
42193         "pt",
42194         "351"
42195       ],
42196       [
42197         "Puerto Rico",
42198         "pr",
42199         "1",
42200         3,
42201         ["787", "939"]
42202       ],
42203       [
42204         "Qatar (‫قطر‬‎)",
42205         "qa",
42206         "974"
42207       ],
42208       [
42209         "Réunion (La Réunion)",
42210         "re",
42211         "262",
42212         0
42213       ],
42214       [
42215         "Romania (România)",
42216         "ro",
42217         "40"
42218       ],
42219       [
42220         "Russia (Россия)",
42221         "ru",
42222         "7",
42223         0
42224       ],
42225       [
42226         "Rwanda",
42227         "rw",
42228         "250"
42229       ],
42230       [
42231         "Saint Barthélemy",
42232         "bl",
42233         "590",
42234         1
42235       ],
42236       [
42237         "Saint Helena",
42238         "sh",
42239         "290"
42240       ],
42241       [
42242         "Saint Kitts and Nevis",
42243         "kn",
42244         "1869"
42245       ],
42246       [
42247         "Saint Lucia",
42248         "lc",
42249         "1758"
42250       ],
42251       [
42252         "Saint Martin (Saint-Martin (partie française))",
42253         "mf",
42254         "590",
42255         2
42256       ],
42257       [
42258         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42259         "pm",
42260         "508"
42261       ],
42262       [
42263         "Saint Vincent and the Grenadines",
42264         "vc",
42265         "1784"
42266       ],
42267       [
42268         "Samoa",
42269         "ws",
42270         "685"
42271       ],
42272       [
42273         "San Marino",
42274         "sm",
42275         "378"
42276       ],
42277       [
42278         "São Tomé and Príncipe (São Tomé e Príncipe)",
42279         "st",
42280         "239"
42281       ],
42282       [
42283         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42284         "sa",
42285         "966"
42286       ],
42287       [
42288         "Senegal (Sénégal)",
42289         "sn",
42290         "221"
42291       ],
42292       [
42293         "Serbia (Србија)",
42294         "rs",
42295         "381"
42296       ],
42297       [
42298         "Seychelles",
42299         "sc",
42300         "248"
42301       ],
42302       [
42303         "Sierra Leone",
42304         "sl",
42305         "232"
42306       ],
42307       [
42308         "Singapore",
42309         "sg",
42310         "65"
42311       ],
42312       [
42313         "Sint Maarten",
42314         "sx",
42315         "1721"
42316       ],
42317       [
42318         "Slovakia (Slovensko)",
42319         "sk",
42320         "421"
42321       ],
42322       [
42323         "Slovenia (Slovenija)",
42324         "si",
42325         "386"
42326       ],
42327       [
42328         "Solomon Islands",
42329         "sb",
42330         "677"
42331       ],
42332       [
42333         "Somalia (Soomaaliya)",
42334         "so",
42335         "252"
42336       ],
42337       [
42338         "South Africa",
42339         "za",
42340         "27"
42341       ],
42342       [
42343         "South Korea (대한민국)",
42344         "kr",
42345         "82"
42346       ],
42347       [
42348         "South Sudan (‫جنوب السودان‬‎)",
42349         "ss",
42350         "211"
42351       ],
42352       [
42353         "Spain (España)",
42354         "es",
42355         "34"
42356       ],
42357       [
42358         "Sri Lanka (ශ්‍රී ලංකාව)",
42359         "lk",
42360         "94"
42361       ],
42362       [
42363         "Sudan (‫السودان‬‎)",
42364         "sd",
42365         "249"
42366       ],
42367       [
42368         "Suriname",
42369         "sr",
42370         "597"
42371       ],
42372       [
42373         "Svalbard and Jan Mayen",
42374         "sj",
42375         "47",
42376         1
42377       ],
42378       [
42379         "Swaziland",
42380         "sz",
42381         "268"
42382       ],
42383       [
42384         "Sweden (Sverige)",
42385         "se",
42386         "46"
42387       ],
42388       [
42389         "Switzerland (Schweiz)",
42390         "ch",
42391         "41"
42392       ],
42393       [
42394         "Syria (‫سوريا‬‎)",
42395         "sy",
42396         "963"
42397       ],
42398       [
42399         "Taiwan (台灣)",
42400         "tw",
42401         "886"
42402       ],
42403       [
42404         "Tajikistan",
42405         "tj",
42406         "992"
42407       ],
42408       [
42409         "Tanzania",
42410         "tz",
42411         "255"
42412       ],
42413       [
42414         "Thailand (ไทย)",
42415         "th",
42416         "66"
42417       ],
42418       [
42419         "Timor-Leste",
42420         "tl",
42421         "670"
42422       ],
42423       [
42424         "Togo",
42425         "tg",
42426         "228"
42427       ],
42428       [
42429         "Tokelau",
42430         "tk",
42431         "690"
42432       ],
42433       [
42434         "Tonga",
42435         "to",
42436         "676"
42437       ],
42438       [
42439         "Trinidad and Tobago",
42440         "tt",
42441         "1868"
42442       ],
42443       [
42444         "Tunisia (‫تونس‬‎)",
42445         "tn",
42446         "216"
42447       ],
42448       [
42449         "Turkey (Türkiye)",
42450         "tr",
42451         "90"
42452       ],
42453       [
42454         "Turkmenistan",
42455         "tm",
42456         "993"
42457       ],
42458       [
42459         "Turks and Caicos Islands",
42460         "tc",
42461         "1649"
42462       ],
42463       [
42464         "Tuvalu",
42465         "tv",
42466         "688"
42467       ],
42468       [
42469         "U.S. Virgin Islands",
42470         "vi",
42471         "1340"
42472       ],
42473       [
42474         "Uganda",
42475         "ug",
42476         "256"
42477       ],
42478       [
42479         "Ukraine (Україна)",
42480         "ua",
42481         "380"
42482       ],
42483       [
42484         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42485         "ae",
42486         "971"
42487       ],
42488       [
42489         "United Kingdom",
42490         "gb",
42491         "44",
42492         0
42493       ],
42494       [
42495         "United States",
42496         "us",
42497         "1",
42498         0
42499       ],
42500       [
42501         "Uruguay",
42502         "uy",
42503         "598"
42504       ],
42505       [
42506         "Uzbekistan (Oʻzbekiston)",
42507         "uz",
42508         "998"
42509       ],
42510       [
42511         "Vanuatu",
42512         "vu",
42513         "678"
42514       ],
42515       [
42516         "Vatican City (Città del Vaticano)",
42517         "va",
42518         "39",
42519         1
42520       ],
42521       [
42522         "Venezuela",
42523         "ve",
42524         "58"
42525       ],
42526       [
42527         "Vietnam (Việt Nam)",
42528         "vn",
42529         "84"
42530       ],
42531       [
42532         "Wallis and Futuna (Wallis-et-Futuna)",
42533         "wf",
42534         "681"
42535       ],
42536       [
42537         "Western Sahara (‫الصحراء الغربية‬‎)",
42538         "eh",
42539         "212",
42540         1
42541       ],
42542       [
42543         "Yemen (‫اليمن‬‎)",
42544         "ye",
42545         "967"
42546       ],
42547       [
42548         "Zambia",
42549         "zm",
42550         "260"
42551       ],
42552       [
42553         "Zimbabwe",
42554         "zw",
42555         "263"
42556       ],
42557       [
42558         "Åland Islands",
42559         "ax",
42560         "358",
42561         1
42562       ]
42563   ];
42564   
42565   return d;
42566 }/**
42567 *    This script refer to:
42568 *    Title: International Telephone Input
42569 *    Author: Jack O'Connor
42570 *    Code version:  v12.1.12
42571 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42572 **/
42573
42574 /**
42575  * @class Roo.bootstrap.PhoneInput
42576  * @extends Roo.bootstrap.TriggerField
42577  * An input with International dial-code selection
42578  
42579  * @cfg {String} defaultDialCode default '+852'
42580  * @cfg {Array} preferedCountries default []
42581   
42582  * @constructor
42583  * Create a new PhoneInput.
42584  * @param {Object} config Configuration options
42585  */
42586
42587 Roo.bootstrap.PhoneInput = function(config) {
42588     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42589 };
42590
42591 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42592         
42593         listWidth: undefined,
42594         
42595         selectedClass: 'active',
42596         
42597         invalidClass : "has-warning",
42598         
42599         validClass: 'has-success',
42600         
42601         allowed: '0123456789',
42602         
42603         max_length: 15,
42604         
42605         /**
42606          * @cfg {String} defaultDialCode The default dial code when initializing the input
42607          */
42608         defaultDialCode: '+852',
42609         
42610         /**
42611          * @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
42612          */
42613         preferedCountries: false,
42614         
42615         getAutoCreate : function()
42616         {
42617             var data = Roo.bootstrap.PhoneInputData();
42618             var align = this.labelAlign || this.parentLabelAlign();
42619             var id = Roo.id();
42620             
42621             this.allCountries = [];
42622             this.dialCodeMapping = [];
42623             
42624             for (var i = 0; i < data.length; i++) {
42625               var c = data[i];
42626               this.allCountries[i] = {
42627                 name: c[0],
42628                 iso2: c[1],
42629                 dialCode: c[2],
42630                 priority: c[3] || 0,
42631                 areaCodes: c[4] || null
42632               };
42633               this.dialCodeMapping[c[2]] = {
42634                   name: c[0],
42635                   iso2: c[1],
42636                   priority: c[3] || 0,
42637                   areaCodes: c[4] || null
42638               };
42639             }
42640             
42641             var cfg = {
42642                 cls: 'form-group',
42643                 cn: []
42644             };
42645             
42646             var input =  {
42647                 tag: 'input',
42648                 id : id,
42649                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42650                 maxlength: this.max_length,
42651                 cls : 'form-control tel-input',
42652                 autocomplete: 'new-password'
42653             };
42654             
42655             var hiddenInput = {
42656                 tag: 'input',
42657                 type: 'hidden',
42658                 cls: 'hidden-tel-input'
42659             };
42660             
42661             if (this.name) {
42662                 hiddenInput.name = this.name;
42663             }
42664             
42665             if (this.disabled) {
42666                 input.disabled = true;
42667             }
42668             
42669             var flag_container = {
42670                 tag: 'div',
42671                 cls: 'flag-box',
42672                 cn: [
42673                     {
42674                         tag: 'div',
42675                         cls: 'flag'
42676                     },
42677                     {
42678                         tag: 'div',
42679                         cls: 'caret'
42680                     }
42681                 ]
42682             };
42683             
42684             var box = {
42685                 tag: 'div',
42686                 cls: this.hasFeedback ? 'has-feedback' : '',
42687                 cn: [
42688                     hiddenInput,
42689                     input,
42690                     {
42691                         tag: 'input',
42692                         cls: 'dial-code-holder',
42693                         disabled: true
42694                     }
42695                 ]
42696             };
42697             
42698             var container = {
42699                 cls: 'roo-select2-container input-group',
42700                 cn: [
42701                     flag_container,
42702                     box
42703                 ]
42704             };
42705             
42706             if (this.fieldLabel.length) {
42707                 var indicator = {
42708                     tag: 'i',
42709                     tooltip: 'This field is required'
42710                 };
42711                 
42712                 var label = {
42713                     tag: 'label',
42714                     'for':  id,
42715                     cls: 'control-label',
42716                     cn: []
42717                 };
42718                 
42719                 var label_text = {
42720                     tag: 'span',
42721                     html: this.fieldLabel
42722                 };
42723                 
42724                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42725                 label.cn = [
42726                     indicator,
42727                     label_text
42728                 ];
42729                 
42730                 if(this.indicatorpos == 'right') {
42731                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42732                     label.cn = [
42733                         label_text,
42734                         indicator
42735                     ];
42736                 }
42737                 
42738                 if(align == 'left') {
42739                     container = {
42740                         tag: 'div',
42741                         cn: [
42742                             container
42743                         ]
42744                     };
42745                     
42746                     if(this.labelWidth > 12){
42747                         label.style = "width: " + this.labelWidth + 'px';
42748                     }
42749                     if(this.labelWidth < 13 && this.labelmd == 0){
42750                         this.labelmd = this.labelWidth;
42751                     }
42752                     if(this.labellg > 0){
42753                         label.cls += ' col-lg-' + this.labellg;
42754                         input.cls += ' col-lg-' + (12 - this.labellg);
42755                     }
42756                     if(this.labelmd > 0){
42757                         label.cls += ' col-md-' + this.labelmd;
42758                         container.cls += ' col-md-' + (12 - this.labelmd);
42759                     }
42760                     if(this.labelsm > 0){
42761                         label.cls += ' col-sm-' + this.labelsm;
42762                         container.cls += ' col-sm-' + (12 - this.labelsm);
42763                     }
42764                     if(this.labelxs > 0){
42765                         label.cls += ' col-xs-' + this.labelxs;
42766                         container.cls += ' col-xs-' + (12 - this.labelxs);
42767                     }
42768                 }
42769             }
42770             
42771             cfg.cn = [
42772                 label,
42773                 container
42774             ];
42775             
42776             var settings = this;
42777             
42778             ['xs','sm','md','lg'].map(function(size){
42779                 if (settings[size]) {
42780                     cfg.cls += ' col-' + size + '-' + settings[size];
42781                 }
42782             });
42783             
42784             this.store = new Roo.data.Store({
42785                 proxy : new Roo.data.MemoryProxy({}),
42786                 reader : new Roo.data.JsonReader({
42787                     fields : [
42788                         {
42789                             'name' : 'name',
42790                             'type' : 'string'
42791                         },
42792                         {
42793                             'name' : 'iso2',
42794                             'type' : 'string'
42795                         },
42796                         {
42797                             'name' : 'dialCode',
42798                             'type' : 'string'
42799                         },
42800                         {
42801                             'name' : 'priority',
42802                             'type' : 'string'
42803                         },
42804                         {
42805                             'name' : 'areaCodes',
42806                             'type' : 'string'
42807                         }
42808                     ]
42809                 })
42810             });
42811             
42812             if(!this.preferedCountries) {
42813                 this.preferedCountries = [
42814                     'hk',
42815                     'gb',
42816                     'us'
42817                 ];
42818             }
42819             
42820             var p = this.preferedCountries.reverse();
42821             
42822             if(p) {
42823                 for (var i = 0; i < p.length; i++) {
42824                     for (var j = 0; j < this.allCountries.length; j++) {
42825                         if(this.allCountries[j].iso2 == p[i]) {
42826                             var t = this.allCountries[j];
42827                             this.allCountries.splice(j,1);
42828                             this.allCountries.unshift(t);
42829                         }
42830                     } 
42831                 }
42832             }
42833             
42834             this.store.proxy.data = {
42835                 success: true,
42836                 data: this.allCountries
42837             };
42838             
42839             return cfg;
42840         },
42841         
42842         initEvents : function()
42843         {
42844             this.createList();
42845             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42846             
42847             this.indicator = this.indicatorEl();
42848             this.flag = this.flagEl();
42849             this.dialCodeHolder = this.dialCodeHolderEl();
42850             
42851             this.trigger = this.el.select('div.flag-box',true).first();
42852             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42853             
42854             var _this = this;
42855             
42856             (function(){
42857                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42858                 _this.list.setWidth(lw);
42859             }).defer(100);
42860             
42861             this.list.on('mouseover', this.onViewOver, this);
42862             this.list.on('mousemove', this.onViewMove, this);
42863             this.inputEl().on("keyup", this.onKeyUp, this);
42864             this.inputEl().on("keypress", this.onKeyPress, this);
42865             
42866             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42867
42868             this.view = new Roo.View(this.list, this.tpl, {
42869                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42870             });
42871             
42872             this.view.on('click', this.onViewClick, this);
42873             this.setValue(this.defaultDialCode);
42874         },
42875         
42876         onTriggerClick : function(e)
42877         {
42878             Roo.log('trigger click');
42879             if(this.disabled){
42880                 return;
42881             }
42882             
42883             if(this.isExpanded()){
42884                 this.collapse();
42885                 this.hasFocus = false;
42886             }else {
42887                 this.store.load({});
42888                 this.hasFocus = true;
42889                 this.expand();
42890             }
42891         },
42892         
42893         isExpanded : function()
42894         {
42895             return this.list.isVisible();
42896         },
42897         
42898         collapse : function()
42899         {
42900             if(!this.isExpanded()){
42901                 return;
42902             }
42903             this.list.hide();
42904             Roo.get(document).un('mousedown', this.collapseIf, this);
42905             Roo.get(document).un('mousewheel', this.collapseIf, this);
42906             this.fireEvent('collapse', this);
42907             this.validate();
42908         },
42909         
42910         expand : function()
42911         {
42912             Roo.log('expand');
42913
42914             if(this.isExpanded() || !this.hasFocus){
42915                 return;
42916             }
42917             
42918             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42919             this.list.setWidth(lw);
42920             
42921             this.list.show();
42922             this.restrictHeight();
42923             
42924             Roo.get(document).on('mousedown', this.collapseIf, this);
42925             Roo.get(document).on('mousewheel', this.collapseIf, this);
42926             
42927             this.fireEvent('expand', this);
42928         },
42929         
42930         restrictHeight : function()
42931         {
42932             this.list.alignTo(this.inputEl(), this.listAlign);
42933             this.list.alignTo(this.inputEl(), this.listAlign);
42934         },
42935         
42936         onViewOver : function(e, t)
42937         {
42938             if(this.inKeyMode){
42939                 return;
42940             }
42941             var item = this.view.findItemFromChild(t);
42942             
42943             if(item){
42944                 var index = this.view.indexOf(item);
42945                 this.select(index, false);
42946             }
42947         },
42948
42949         // private
42950         onViewClick : function(view, doFocus, el, e)
42951         {
42952             var index = this.view.getSelectedIndexes()[0];
42953             
42954             var r = this.store.getAt(index);
42955             
42956             if(r){
42957                 this.onSelect(r, index);
42958             }
42959             if(doFocus !== false && !this.blockFocus){
42960                 this.inputEl().focus();
42961             }
42962         },
42963         
42964         onViewMove : function(e, t)
42965         {
42966             this.inKeyMode = false;
42967         },
42968         
42969         select : function(index, scrollIntoView)
42970         {
42971             this.selectedIndex = index;
42972             this.view.select(index);
42973             if(scrollIntoView !== false){
42974                 var el = this.view.getNode(index);
42975                 if(el){
42976                     this.list.scrollChildIntoView(el, false);
42977                 }
42978             }
42979         },
42980         
42981         createList : function()
42982         {
42983             this.list = Roo.get(document.body).createChild({
42984                 tag: 'ul',
42985                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42986                 style: 'display:none'
42987             });
42988             
42989             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42990         },
42991         
42992         collapseIf : function(e)
42993         {
42994             var in_combo  = e.within(this.el);
42995             var in_list =  e.within(this.list);
42996             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42997             
42998             if (in_combo || in_list || is_list) {
42999                 return;
43000             }
43001             this.collapse();
43002         },
43003         
43004         onSelect : function(record, index)
43005         {
43006             if(this.fireEvent('beforeselect', this, record, index) !== false){
43007                 
43008                 this.setFlagClass(record.data.iso2);
43009                 this.setDialCode(record.data.dialCode);
43010                 this.hasFocus = false;
43011                 this.collapse();
43012                 this.fireEvent('select', this, record, index);
43013             }
43014         },
43015         
43016         flagEl : function()
43017         {
43018             var flag = this.el.select('div.flag',true).first();
43019             if(!flag){
43020                 return false;
43021             }
43022             return flag;
43023         },
43024         
43025         dialCodeHolderEl : function()
43026         {
43027             var d = this.el.select('input.dial-code-holder',true).first();
43028             if(!d){
43029                 return false;
43030             }
43031             return d;
43032         },
43033         
43034         setDialCode : function(v)
43035         {
43036             this.dialCodeHolder.dom.value = '+'+v;
43037         },
43038         
43039         setFlagClass : function(n)
43040         {
43041             this.flag.dom.className = 'flag '+n;
43042         },
43043         
43044         getValue : function()
43045         {
43046             var v = this.inputEl().getValue();
43047             if(this.dialCodeHolder) {
43048                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43049             }
43050             return v;
43051         },
43052         
43053         setValue : function(v)
43054         {
43055             var d = this.getDialCode(v);
43056             
43057             //invalid dial code
43058             if(v.length == 0 || !d || d.length == 0) {
43059                 if(this.rendered){
43060                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43061                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43062                 }
43063                 return;
43064             }
43065             
43066             //valid dial code
43067             this.setFlagClass(this.dialCodeMapping[d].iso2);
43068             this.setDialCode(d);
43069             this.inputEl().dom.value = v.replace('+'+d,'');
43070             this.hiddenEl().dom.value = this.getValue();
43071             
43072             this.validate();
43073         },
43074         
43075         getDialCode : function(v)
43076         {
43077             v = v ||  '';
43078             
43079             if (v.length == 0) {
43080                 return this.dialCodeHolder.dom.value;
43081             }
43082             
43083             var dialCode = "";
43084             if (v.charAt(0) != "+") {
43085                 return false;
43086             }
43087             var numericChars = "";
43088             for (var i = 1; i < v.length; i++) {
43089               var c = v.charAt(i);
43090               if (!isNaN(c)) {
43091                 numericChars += c;
43092                 if (this.dialCodeMapping[numericChars]) {
43093                   dialCode = v.substr(1, i);
43094                 }
43095                 if (numericChars.length == 4) {
43096                   break;
43097                 }
43098               }
43099             }
43100             return dialCode;
43101         },
43102         
43103         reset : function()
43104         {
43105             this.setValue(this.defaultDialCode);
43106             this.validate();
43107         },
43108         
43109         hiddenEl : function()
43110         {
43111             return this.el.select('input.hidden-tel-input',true).first();
43112         },
43113         
43114         // after setting val
43115         onKeyUp : function(e){
43116             this.setValue(this.getValue());
43117         },
43118         
43119         onKeyPress : function(e){
43120             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43121                 e.stopEvent();
43122             }
43123         }
43124         
43125 });
43126 /**
43127  * @class Roo.bootstrap.MoneyField
43128  * @extends Roo.bootstrap.ComboBox
43129  * Bootstrap MoneyField class
43130  * 
43131  * @constructor
43132  * Create a new MoneyField.
43133  * @param {Object} config Configuration options
43134  */
43135
43136 Roo.bootstrap.MoneyField = function(config) {
43137     
43138     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43139     
43140 };
43141
43142 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43143     
43144     /**
43145      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43146      */
43147     allowDecimals : true,
43148     /**
43149      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43150      */
43151     decimalSeparator : ".",
43152     /**
43153      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43154      */
43155     decimalPrecision : 0,
43156     /**
43157      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43158      */
43159     allowNegative : true,
43160     /**
43161      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43162      */
43163     allowZero: true,
43164     /**
43165      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43166      */
43167     minValue : Number.NEGATIVE_INFINITY,
43168     /**
43169      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43170      */
43171     maxValue : Number.MAX_VALUE,
43172     /**
43173      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43174      */
43175     minText : "The minimum value for this field is {0}",
43176     /**
43177      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43178      */
43179     maxText : "The maximum value for this field is {0}",
43180     /**
43181      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43182      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43183      */
43184     nanText : "{0} is not a valid number",
43185     /**
43186      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43187      */
43188     castInt : true,
43189     /**
43190      * @cfg {String} defaults currency of the MoneyField
43191      * value should be in lkey
43192      */
43193     defaultCurrency : false,
43194     /**
43195      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43196      */
43197     thousandsDelimiter : false,
43198     /**
43199      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43200      */
43201     max_length: false,
43202     
43203     inputlg : 9,
43204     inputmd : 9,
43205     inputsm : 9,
43206     inputxs : 6,
43207     
43208     store : false,
43209     
43210     getAutoCreate : function()
43211     {
43212         var align = this.labelAlign || this.parentLabelAlign();
43213         
43214         var id = Roo.id();
43215
43216         var cfg = {
43217             cls: 'form-group',
43218             cn: []
43219         };
43220
43221         var input =  {
43222             tag: 'input',
43223             id : id,
43224             cls : 'form-control roo-money-amount-input',
43225             autocomplete: 'new-password'
43226         };
43227         
43228         var hiddenInput = {
43229             tag: 'input',
43230             type: 'hidden',
43231             id: Roo.id(),
43232             cls: 'hidden-number-input'
43233         };
43234         
43235         if(this.max_length) {
43236             input.maxlength = this.max_length; 
43237         }
43238         
43239         if (this.name) {
43240             hiddenInput.name = this.name;
43241         }
43242
43243         if (this.disabled) {
43244             input.disabled = true;
43245         }
43246
43247         var clg = 12 - this.inputlg;
43248         var cmd = 12 - this.inputmd;
43249         var csm = 12 - this.inputsm;
43250         var cxs = 12 - this.inputxs;
43251         
43252         var container = {
43253             tag : 'div',
43254             cls : 'row roo-money-field',
43255             cn : [
43256                 {
43257                     tag : 'div',
43258                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43259                     cn : [
43260                         {
43261                             tag : 'div',
43262                             cls: 'roo-select2-container input-group',
43263                             cn: [
43264                                 {
43265                                     tag : 'input',
43266                                     cls : 'form-control roo-money-currency-input',
43267                                     autocomplete: 'new-password',
43268                                     readOnly : 1,
43269                                     name : this.currencyName
43270                                 },
43271                                 {
43272                                     tag :'span',
43273                                     cls : 'input-group-addon',
43274                                     cn : [
43275                                         {
43276                                             tag: 'span',
43277                                             cls: 'caret'
43278                                         }
43279                                     ]
43280                                 }
43281                             ]
43282                         }
43283                     ]
43284                 },
43285                 {
43286                     tag : 'div',
43287                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43288                     cn : [
43289                         {
43290                             tag: 'div',
43291                             cls: this.hasFeedback ? 'has-feedback' : '',
43292                             cn: [
43293                                 input
43294                             ]
43295                         }
43296                     ]
43297                 }
43298             ]
43299             
43300         };
43301         
43302         if (this.fieldLabel.length) {
43303             var indicator = {
43304                 tag: 'i',
43305                 tooltip: 'This field is required'
43306             };
43307
43308             var label = {
43309                 tag: 'label',
43310                 'for':  id,
43311                 cls: 'control-label',
43312                 cn: []
43313             };
43314
43315             var label_text = {
43316                 tag: 'span',
43317                 html: this.fieldLabel
43318             };
43319
43320             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43321             label.cn = [
43322                 indicator,
43323                 label_text
43324             ];
43325
43326             if(this.indicatorpos == 'right') {
43327                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43328                 label.cn = [
43329                     label_text,
43330                     indicator
43331                 ];
43332             }
43333
43334             if(align == 'left') {
43335                 container = {
43336                     tag: 'div',
43337                     cn: [
43338                         container
43339                     ]
43340                 };
43341
43342                 if(this.labelWidth > 12){
43343                     label.style = "width: " + this.labelWidth + 'px';
43344                 }
43345                 if(this.labelWidth < 13 && this.labelmd == 0){
43346                     this.labelmd = this.labelWidth;
43347                 }
43348                 if(this.labellg > 0){
43349                     label.cls += ' col-lg-' + this.labellg;
43350                     input.cls += ' col-lg-' + (12 - this.labellg);
43351                 }
43352                 if(this.labelmd > 0){
43353                     label.cls += ' col-md-' + this.labelmd;
43354                     container.cls += ' col-md-' + (12 - this.labelmd);
43355                 }
43356                 if(this.labelsm > 0){
43357                     label.cls += ' col-sm-' + this.labelsm;
43358                     container.cls += ' col-sm-' + (12 - this.labelsm);
43359                 }
43360                 if(this.labelxs > 0){
43361                     label.cls += ' col-xs-' + this.labelxs;
43362                     container.cls += ' col-xs-' + (12 - this.labelxs);
43363                 }
43364             }
43365         }
43366
43367         cfg.cn = [
43368             label,
43369             container,
43370             hiddenInput
43371         ];
43372         
43373         var settings = this;
43374
43375         ['xs','sm','md','lg'].map(function(size){
43376             if (settings[size]) {
43377                 cfg.cls += ' col-' + size + '-' + settings[size];
43378             }
43379         });
43380         
43381         return cfg;
43382     },
43383     
43384     initEvents : function()
43385     {
43386         this.indicator = this.indicatorEl();
43387         
43388         this.initCurrencyEvent();
43389         
43390         this.initNumberEvent();
43391     },
43392     
43393     initCurrencyEvent : function()
43394     {
43395         if (!this.store) {
43396             throw "can not find store for combo";
43397         }
43398         
43399         this.store = Roo.factory(this.store, Roo.data);
43400         this.store.parent = this;
43401         
43402         this.createList();
43403         
43404         this.triggerEl = this.el.select('.input-group-addon', true).first();
43405         
43406         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43407         
43408         var _this = this;
43409         
43410         (function(){
43411             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43412             _this.list.setWidth(lw);
43413         }).defer(100);
43414         
43415         this.list.on('mouseover', this.onViewOver, this);
43416         this.list.on('mousemove', this.onViewMove, this);
43417         this.list.on('scroll', this.onViewScroll, this);
43418         
43419         if(!this.tpl){
43420             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43421         }
43422         
43423         this.view = new Roo.View(this.list, this.tpl, {
43424             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43425         });
43426         
43427         this.view.on('click', this.onViewClick, this);
43428         
43429         this.store.on('beforeload', this.onBeforeLoad, this);
43430         this.store.on('load', this.onLoad, this);
43431         this.store.on('loadexception', this.onLoadException, this);
43432         
43433         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43434             "up" : function(e){
43435                 this.inKeyMode = true;
43436                 this.selectPrev();
43437             },
43438
43439             "down" : function(e){
43440                 if(!this.isExpanded()){
43441                     this.onTriggerClick();
43442                 }else{
43443                     this.inKeyMode = true;
43444                     this.selectNext();
43445                 }
43446             },
43447
43448             "enter" : function(e){
43449                 this.collapse();
43450                 
43451                 if(this.fireEvent("specialkey", this, e)){
43452                     this.onViewClick(false);
43453                 }
43454                 
43455                 return true;
43456             },
43457
43458             "esc" : function(e){
43459                 this.collapse();
43460             },
43461
43462             "tab" : function(e){
43463                 this.collapse();
43464                 
43465                 if(this.fireEvent("specialkey", this, e)){
43466                     this.onViewClick(false);
43467                 }
43468                 
43469                 return true;
43470             },
43471
43472             scope : this,
43473
43474             doRelay : function(foo, bar, hname){
43475                 if(hname == 'down' || this.scope.isExpanded()){
43476                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43477                 }
43478                 return true;
43479             },
43480
43481             forceKeyDown: true
43482         });
43483         
43484         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43485         
43486     },
43487     
43488     initNumberEvent : function(e)
43489     {
43490         this.inputEl().on("keydown" , this.fireKey,  this);
43491         this.inputEl().on("focus", this.onFocus,  this);
43492         this.inputEl().on("blur", this.onBlur,  this);
43493         
43494         this.inputEl().relayEvent('keyup', this);
43495         
43496         if(this.indicator){
43497             this.indicator.addClass('invisible');
43498         }
43499  
43500         this.originalValue = this.getValue();
43501         
43502         if(this.validationEvent == 'keyup'){
43503             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43504             this.inputEl().on('keyup', this.filterValidation, this);
43505         }
43506         else if(this.validationEvent !== false){
43507             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43508         }
43509         
43510         if(this.selectOnFocus){
43511             this.on("focus", this.preFocus, this);
43512             
43513         }
43514         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43515             this.inputEl().on("keypress", this.filterKeys, this);
43516         } else {
43517             this.inputEl().relayEvent('keypress', this);
43518         }
43519         
43520         var allowed = "0123456789";
43521         
43522         if(this.allowDecimals){
43523             allowed += this.decimalSeparator;
43524         }
43525         
43526         if(this.allowNegative){
43527             allowed += "-";
43528         }
43529         
43530         if(this.thousandsDelimiter) {
43531             allowed += ",";
43532         }
43533         
43534         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43535         
43536         var keyPress = function(e){
43537             
43538             var k = e.getKey();
43539             
43540             var c = e.getCharCode();
43541             
43542             if(
43543                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43544                     allowed.indexOf(String.fromCharCode(c)) === -1
43545             ){
43546                 e.stopEvent();
43547                 return;
43548             }
43549             
43550             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43551                 return;
43552             }
43553             
43554             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43555                 e.stopEvent();
43556             }
43557         };
43558         
43559         this.inputEl().on("keypress", keyPress, this);
43560         
43561     },
43562     
43563     onTriggerClick : function(e)
43564     {   
43565         if(this.disabled){
43566             return;
43567         }
43568         
43569         this.page = 0;
43570         this.loadNext = false;
43571         
43572         if(this.isExpanded()){
43573             this.collapse();
43574             return;
43575         }
43576         
43577         this.hasFocus = true;
43578         
43579         if(this.triggerAction == 'all') {
43580             this.doQuery(this.allQuery, true);
43581             return;
43582         }
43583         
43584         this.doQuery(this.getRawValue());
43585     },
43586     
43587     getCurrency : function()
43588     {   
43589         var v = this.currencyEl().getValue();
43590         
43591         return v;
43592     },
43593     
43594     restrictHeight : function()
43595     {
43596         this.list.alignTo(this.currencyEl(), this.listAlign);
43597         this.list.alignTo(this.currencyEl(), this.listAlign);
43598     },
43599     
43600     onViewClick : function(view, doFocus, el, e)
43601     {
43602         var index = this.view.getSelectedIndexes()[0];
43603         
43604         var r = this.store.getAt(index);
43605         
43606         if(r){
43607             this.onSelect(r, index);
43608         }
43609     },
43610     
43611     onSelect : function(record, index){
43612         
43613         if(this.fireEvent('beforeselect', this, record, index) !== false){
43614         
43615             this.setFromCurrencyData(index > -1 ? record.data : false);
43616             
43617             this.collapse();
43618             
43619             this.fireEvent('select', this, record, index);
43620         }
43621     },
43622     
43623     setFromCurrencyData : function(o)
43624     {
43625         var currency = '';
43626         
43627         this.lastCurrency = o;
43628         
43629         if (this.currencyField) {
43630             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43631         } else {
43632             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43633         }
43634         
43635         this.lastSelectionText = currency;
43636         
43637         //setting default currency
43638         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43639             this.setCurrency(this.defaultCurrency);
43640             return;
43641         }
43642         
43643         this.setCurrency(currency);
43644     },
43645     
43646     setFromData : function(o)
43647     {
43648         var c = {};
43649         
43650         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43651         
43652         this.setFromCurrencyData(c);
43653         
43654         var value = '';
43655         
43656         if (this.name) {
43657             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43658         } else {
43659             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43660         }
43661         
43662         this.setValue(value);
43663         
43664     },
43665     
43666     setCurrency : function(v)
43667     {   
43668         this.currencyValue = v;
43669         
43670         if(this.rendered){
43671             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43672             this.validate();
43673         }
43674     },
43675     
43676     setValue : function(v)
43677     {
43678         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43679         
43680         this.value = v;
43681         
43682         if(this.rendered){
43683             
43684             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43685             
43686             this.inputEl().dom.value = (v == '') ? '' :
43687                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43688             
43689             if(!this.allowZero && v === '0') {
43690                 this.hiddenEl().dom.value = '';
43691                 this.inputEl().dom.value = '';
43692             }
43693             
43694             this.validate();
43695         }
43696     },
43697     
43698     getRawValue : function()
43699     {
43700         var v = this.inputEl().getValue();
43701         
43702         return v;
43703     },
43704     
43705     getValue : function()
43706     {
43707         return this.fixPrecision(this.parseValue(this.getRawValue()));
43708     },
43709     
43710     parseValue : function(value)
43711     {
43712         if(this.thousandsDelimiter) {
43713             value += "";
43714             r = new RegExp(",", "g");
43715             value = value.replace(r, "");
43716         }
43717         
43718         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43719         return isNaN(value) ? '' : value;
43720         
43721     },
43722     
43723     fixPrecision : function(value)
43724     {
43725         if(this.thousandsDelimiter) {
43726             value += "";
43727             r = new RegExp(",", "g");
43728             value = value.replace(r, "");
43729         }
43730         
43731         var nan = isNaN(value);
43732         
43733         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43734             return nan ? '' : value;
43735         }
43736         return parseFloat(value).toFixed(this.decimalPrecision);
43737     },
43738     
43739     decimalPrecisionFcn : function(v)
43740     {
43741         return Math.floor(v);
43742     },
43743     
43744     validateValue : function(value)
43745     {
43746         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43747             return false;
43748         }
43749         
43750         var num = this.parseValue(value);
43751         
43752         if(isNaN(num)){
43753             this.markInvalid(String.format(this.nanText, value));
43754             return false;
43755         }
43756         
43757         if(num < this.minValue){
43758             this.markInvalid(String.format(this.minText, this.minValue));
43759             return false;
43760         }
43761         
43762         if(num > this.maxValue){
43763             this.markInvalid(String.format(this.maxText, this.maxValue));
43764             return false;
43765         }
43766         
43767         return true;
43768     },
43769     
43770     validate : function()
43771     {
43772         if(this.disabled || this.allowBlank){
43773             this.markValid();
43774             return true;
43775         }
43776         
43777         var currency = this.getCurrency();
43778         
43779         if(this.validateValue(this.getRawValue()) && currency.length){
43780             this.markValid();
43781             return true;
43782         }
43783         
43784         this.markInvalid();
43785         return false;
43786     },
43787     
43788     getName: function()
43789     {
43790         return this.name;
43791     },
43792     
43793     beforeBlur : function()
43794     {
43795         if(!this.castInt){
43796             return;
43797         }
43798         
43799         var v = this.parseValue(this.getRawValue());
43800         
43801         if(v || v == 0){
43802             this.setValue(v);
43803         }
43804     },
43805     
43806     onBlur : function()
43807     {
43808         this.beforeBlur();
43809         
43810         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43811             //this.el.removeClass(this.focusClass);
43812         }
43813         
43814         this.hasFocus = false;
43815         
43816         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43817             this.validate();
43818         }
43819         
43820         var v = this.getValue();
43821         
43822         if(String(v) !== String(this.startValue)){
43823             this.fireEvent('change', this, v, this.startValue);
43824         }
43825         
43826         this.fireEvent("blur", this);
43827     },
43828     
43829     inputEl : function()
43830     {
43831         return this.el.select('.roo-money-amount-input', true).first();
43832     },
43833     
43834     currencyEl : function()
43835     {
43836         return this.el.select('.roo-money-currency-input', true).first();
43837     },
43838     
43839     hiddenEl : function()
43840     {
43841         return this.el.select('input.hidden-number-input',true).first();
43842     }
43843     
43844 });/**
43845  * @class Roo.bootstrap.BezierSignature
43846  * @extends Roo.bootstrap.Component
43847  * Bootstrap BezierSignature class
43848  * This script refer to:
43849  *    Title: Signature Pad
43850  *    Author: szimek
43851  *    Availability: https://github.com/szimek/signature_pad
43852  *
43853  * @constructor
43854  * Create a new BezierSignature
43855  * @param {Object} config The config object
43856  */
43857
43858 Roo.bootstrap.BezierSignature = function(config){
43859     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43860     this.addEvents({
43861         "resize" : true
43862     });
43863 };
43864
43865 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43866 {
43867      
43868     curve_data: [],
43869     
43870     is_empty: true,
43871     
43872     mouse_btn_down: true,
43873     
43874     /**
43875      * @cfg {int} canvas height
43876      */
43877     canvas_height: '200px',
43878     
43879     /**
43880      * @cfg {float|function} Radius of a single dot.
43881      */ 
43882     dot_size: false,
43883     
43884     /**
43885      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43886      */
43887     min_width: 0.5,
43888     
43889     /**
43890      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43891      */
43892     max_width: 2.5,
43893     
43894     /**
43895      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43896      */
43897     throttle: 16,
43898     
43899     /**
43900      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43901      */
43902     min_distance: 5,
43903     
43904     /**
43905      * @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.
43906      */
43907     bg_color: 'rgba(0, 0, 0, 0)',
43908     
43909     /**
43910      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43911      */
43912     dot_color: 'black',
43913     
43914     /**
43915      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43916      */ 
43917     velocity_filter_weight: 0.7,
43918     
43919     /**
43920      * @cfg {function} Callback when stroke begin. 
43921      */
43922     onBegin: false,
43923     
43924     /**
43925      * @cfg {function} Callback when stroke end.
43926      */
43927     onEnd: false,
43928     
43929     getAutoCreate : function()
43930     {
43931         var cls = 'roo-signature column';
43932         
43933         if(this.cls){
43934             cls += ' ' + this.cls;
43935         }
43936         
43937         var col_sizes = [
43938             'lg',
43939             'md',
43940             'sm',
43941             'xs'
43942         ];
43943         
43944         for(var i = 0; i < col_sizes.length; i++) {
43945             if(this[col_sizes[i]]) {
43946                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43947             }
43948         }
43949         
43950         var cfg = {
43951             tag: 'div',
43952             cls: cls,
43953             cn: [
43954                 {
43955                     tag: 'div',
43956                     cls: 'roo-signature-body',
43957                     cn: [
43958                         {
43959                             tag: 'canvas',
43960                             cls: 'roo-signature-body-canvas',
43961                             height: this.canvas_height,
43962                             width: this.canvas_width
43963                         }
43964                     ]
43965                 },
43966                 {
43967                     tag: 'input',
43968                     type: 'file',
43969                     style: 'display: none'
43970                 }
43971             ]
43972         };
43973         
43974         return cfg;
43975     },
43976     
43977     initEvents: function() 
43978     {
43979         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43980         
43981         var canvas = this.canvasEl();
43982         
43983         // mouse && touch event swapping...
43984         canvas.dom.style.touchAction = 'none';
43985         canvas.dom.style.msTouchAction = 'none';
43986         
43987         this.mouse_btn_down = false;
43988         canvas.on('mousedown', this._handleMouseDown, this);
43989         canvas.on('mousemove', this._handleMouseMove, this);
43990         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43991         
43992         if (window.PointerEvent) {
43993             canvas.on('pointerdown', this._handleMouseDown, this);
43994             canvas.on('pointermove', this._handleMouseMove, this);
43995             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43996         }
43997         
43998         if ('ontouchstart' in window) {
43999             canvas.on('touchstart', this._handleTouchStart, this);
44000             canvas.on('touchmove', this._handleTouchMove, this);
44001             canvas.on('touchend', this._handleTouchEnd, this);
44002         }
44003         
44004         Roo.EventManager.onWindowResize(this.resize, this, true);
44005         
44006         // file input event
44007         this.fileEl().on('change', this.uploadImage, this);
44008         
44009         this.clear();
44010         
44011         this.resize();
44012     },
44013     
44014     resize: function(){
44015         
44016         var canvas = this.canvasEl().dom;
44017         var ctx = this.canvasElCtx();
44018         var img_data = false;
44019         
44020         if(canvas.width > 0) {
44021             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44022         }
44023         // setting canvas width will clean img data
44024         canvas.width = 0;
44025         
44026         var style = window.getComputedStyle ? 
44027             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44028             
44029         var padding_left = parseInt(style.paddingLeft) || 0;
44030         var padding_right = parseInt(style.paddingRight) || 0;
44031         
44032         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44033         
44034         if(img_data) {
44035             ctx.putImageData(img_data, 0, 0);
44036         }
44037     },
44038     
44039     _handleMouseDown: function(e)
44040     {
44041         if (e.browserEvent.which === 1) {
44042             this.mouse_btn_down = true;
44043             this.strokeBegin(e);
44044         }
44045     },
44046     
44047     _handleMouseMove: function (e)
44048     {
44049         if (this.mouse_btn_down) {
44050             this.strokeMoveUpdate(e);
44051         }
44052     },
44053     
44054     _handleMouseUp: function (e)
44055     {
44056         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44057             this.mouse_btn_down = false;
44058             this.strokeEnd(e);
44059         }
44060     },
44061     
44062     _handleTouchStart: function (e) {
44063         
44064         e.preventDefault();
44065         if (e.browserEvent.targetTouches.length === 1) {
44066             // var touch = e.browserEvent.changedTouches[0];
44067             // this.strokeBegin(touch);
44068             
44069              this.strokeBegin(e); // assume e catching the correct xy...
44070         }
44071     },
44072     
44073     _handleTouchMove: function (e) {
44074         e.preventDefault();
44075         // var touch = event.targetTouches[0];
44076         // _this._strokeMoveUpdate(touch);
44077         this.strokeMoveUpdate(e);
44078     },
44079     
44080     _handleTouchEnd: function (e) {
44081         var wasCanvasTouched = e.target === this.canvasEl().dom;
44082         if (wasCanvasTouched) {
44083             e.preventDefault();
44084             // var touch = event.changedTouches[0];
44085             // _this._strokeEnd(touch);
44086             this.strokeEnd(e);
44087         }
44088     },
44089     
44090     reset: function () {
44091         this._lastPoints = [];
44092         this._lastVelocity = 0;
44093         this._lastWidth = (this.min_width + this.max_width) / 2;
44094         this.canvasElCtx().fillStyle = this.dot_color;
44095     },
44096     
44097     strokeMoveUpdate: function(e)
44098     {
44099         this.strokeUpdate(e);
44100         
44101         if (this.throttle) {
44102             this.throttleStroke(this.strokeUpdate, this.throttle);
44103         }
44104         else {
44105             this.strokeUpdate(e);
44106         }
44107     },
44108     
44109     strokeBegin: function(e)
44110     {
44111         var newPointGroup = {
44112             color: this.dot_color,
44113             points: []
44114         };
44115         
44116         if (typeof this.onBegin === 'function') {
44117             this.onBegin(e);
44118         }
44119         
44120         this.curve_data.push(newPointGroup);
44121         this.reset();
44122         this.strokeUpdate(e);
44123     },
44124     
44125     strokeUpdate: function(e)
44126     {
44127         var rect = this.canvasEl().dom.getBoundingClientRect();
44128         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44129         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44130         var lastPoints = lastPointGroup.points;
44131         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44132         var isLastPointTooClose = lastPoint
44133             ? point.distanceTo(lastPoint) <= this.min_distance
44134             : false;
44135         var color = lastPointGroup.color;
44136         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44137             var curve = this.addPoint(point);
44138             if (!lastPoint) {
44139                 this.drawDot({color: color, point: point});
44140             }
44141             else if (curve) {
44142                 this.drawCurve({color: color, curve: curve});
44143             }
44144             lastPoints.push({
44145                 time: point.time,
44146                 x: point.x,
44147                 y: point.y
44148             });
44149         }
44150     },
44151     
44152     strokeEnd: function(e)
44153     {
44154         this.strokeUpdate(e);
44155         if (typeof this.onEnd === 'function') {
44156             this.onEnd(e);
44157         }
44158     },
44159     
44160     addPoint:  function (point) {
44161         var _lastPoints = this._lastPoints;
44162         _lastPoints.push(point);
44163         if (_lastPoints.length > 2) {
44164             if (_lastPoints.length === 3) {
44165                 _lastPoints.unshift(_lastPoints[0]);
44166             }
44167             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44168             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44169             _lastPoints.shift();
44170             return curve;
44171         }
44172         return null;
44173     },
44174     
44175     calculateCurveWidths: function (startPoint, endPoint) {
44176         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44177             (1 - this.velocity_filter_weight) * this._lastVelocity;
44178
44179         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44180         var widths = {
44181             end: newWidth,
44182             start: this._lastWidth
44183         };
44184         
44185         this._lastVelocity = velocity;
44186         this._lastWidth = newWidth;
44187         return widths;
44188     },
44189     
44190     drawDot: function (_a) {
44191         var color = _a.color, point = _a.point;
44192         var ctx = this.canvasElCtx();
44193         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44194         ctx.beginPath();
44195         this.drawCurveSegment(point.x, point.y, width);
44196         ctx.closePath();
44197         ctx.fillStyle = color;
44198         ctx.fill();
44199     },
44200     
44201     drawCurve: function (_a) {
44202         var color = _a.color, curve = _a.curve;
44203         var ctx = this.canvasElCtx();
44204         var widthDelta = curve.endWidth - curve.startWidth;
44205         var drawSteps = Math.floor(curve.length()) * 2;
44206         ctx.beginPath();
44207         ctx.fillStyle = color;
44208         for (var i = 0; i < drawSteps; i += 1) {
44209         var t = i / drawSteps;
44210         var tt = t * t;
44211         var ttt = tt * t;
44212         var u = 1 - t;
44213         var uu = u * u;
44214         var uuu = uu * u;
44215         var x = uuu * curve.startPoint.x;
44216         x += 3 * uu * t * curve.control1.x;
44217         x += 3 * u * tt * curve.control2.x;
44218         x += ttt * curve.endPoint.x;
44219         var y = uuu * curve.startPoint.y;
44220         y += 3 * uu * t * curve.control1.y;
44221         y += 3 * u * tt * curve.control2.y;
44222         y += ttt * curve.endPoint.y;
44223         var width = curve.startWidth + ttt * widthDelta;
44224         this.drawCurveSegment(x, y, width);
44225         }
44226         ctx.closePath();
44227         ctx.fill();
44228     },
44229     
44230     drawCurveSegment: function (x, y, width) {
44231         var ctx = this.canvasElCtx();
44232         ctx.moveTo(x, y);
44233         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44234         this.is_empty = false;
44235     },
44236     
44237     clear: function()
44238     {
44239         var ctx = this.canvasElCtx();
44240         var canvas = this.canvasEl().dom;
44241         ctx.fillStyle = this.bg_color;
44242         ctx.clearRect(0, 0, canvas.width, canvas.height);
44243         ctx.fillRect(0, 0, canvas.width, canvas.height);
44244         this.curve_data = [];
44245         this.reset();
44246         this.is_empty = true;
44247     },
44248     
44249     fileEl: function()
44250     {
44251         return  this.el.select('input',true).first();
44252     },
44253     
44254     canvasEl: function()
44255     {
44256         return this.el.select('canvas',true).first();
44257     },
44258     
44259     canvasElCtx: function()
44260     {
44261         return this.el.select('canvas',true).first().dom.getContext('2d');
44262     },
44263     
44264     getImage: function(type)
44265     {
44266         if(this.is_empty) {
44267             return false;
44268         }
44269         
44270         // encryption ?
44271         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44272     },
44273     
44274     drawFromImage: function(img_src)
44275     {
44276         var img = new Image();
44277         
44278         img.onload = function(){
44279             this.canvasElCtx().drawImage(img, 0, 0);
44280         }.bind(this);
44281         
44282         img.src = img_src;
44283         
44284         this.is_empty = false;
44285     },
44286     
44287     selectImage: function()
44288     {
44289         this.fileEl().dom.click();
44290     },
44291     
44292     uploadImage: function(e)
44293     {
44294         var reader = new FileReader();
44295         
44296         reader.onload = function(e){
44297             var img = new Image();
44298             img.onload = function(){
44299                 this.reset();
44300                 this.canvasElCtx().drawImage(img, 0, 0);
44301             }.bind(this);
44302             img.src = e.target.result;
44303         }.bind(this);
44304         
44305         reader.readAsDataURL(e.target.files[0]);
44306     },
44307     
44308     // Bezier Point Constructor
44309     Point: (function () {
44310         function Point(x, y, time) {
44311             this.x = x;
44312             this.y = y;
44313             this.time = time || Date.now();
44314         }
44315         Point.prototype.distanceTo = function (start) {
44316             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44317         };
44318         Point.prototype.equals = function (other) {
44319             return this.x === other.x && this.y === other.y && this.time === other.time;
44320         };
44321         Point.prototype.velocityFrom = function (start) {
44322             return this.time !== start.time
44323             ? this.distanceTo(start) / (this.time - start.time)
44324             : 0;
44325         };
44326         return Point;
44327     }()),
44328     
44329     
44330     // Bezier Constructor
44331     Bezier: (function () {
44332         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44333             this.startPoint = startPoint;
44334             this.control2 = control2;
44335             this.control1 = control1;
44336             this.endPoint = endPoint;
44337             this.startWidth = startWidth;
44338             this.endWidth = endWidth;
44339         }
44340         Bezier.fromPoints = function (points, widths, scope) {
44341             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44342             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44343             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44344         };
44345         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44346             var dx1 = s1.x - s2.x;
44347             var dy1 = s1.y - s2.y;
44348             var dx2 = s2.x - s3.x;
44349             var dy2 = s2.y - s3.y;
44350             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44351             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44352             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44353             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44354             var dxm = m1.x - m2.x;
44355             var dym = m1.y - m2.y;
44356             var k = l2 / (l1 + l2);
44357             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44358             var tx = s2.x - cm.x;
44359             var ty = s2.y - cm.y;
44360             return {
44361                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44362                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44363             };
44364         };
44365         Bezier.prototype.length = function () {
44366             var steps = 10;
44367             var length = 0;
44368             var px;
44369             var py;
44370             for (var i = 0; i <= steps; i += 1) {
44371                 var t = i / steps;
44372                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44373                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44374                 if (i > 0) {
44375                     var xdiff = cx - px;
44376                     var ydiff = cy - py;
44377                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44378                 }
44379                 px = cx;
44380                 py = cy;
44381             }
44382             return length;
44383         };
44384         Bezier.prototype.point = function (t, start, c1, c2, end) {
44385             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44386             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44387             + (3.0 * c2 * (1.0 - t) * t * t)
44388             + (end * t * t * t);
44389         };
44390         return Bezier;
44391     }()),
44392     
44393     throttleStroke: function(fn, wait) {
44394       if (wait === void 0) { wait = 250; }
44395       var previous = 0;
44396       var timeout = null;
44397       var result;
44398       var storedContext;
44399       var storedArgs;
44400       var later = function () {
44401           previous = Date.now();
44402           timeout = null;
44403           result = fn.apply(storedContext, storedArgs);
44404           if (!timeout) {
44405               storedContext = null;
44406               storedArgs = [];
44407           }
44408       };
44409       return function wrapper() {
44410           var args = [];
44411           for (var _i = 0; _i < arguments.length; _i++) {
44412               args[_i] = arguments[_i];
44413           }
44414           var now = Date.now();
44415           var remaining = wait - (now - previous);
44416           storedContext = this;
44417           storedArgs = args;
44418           if (remaining <= 0 || remaining > wait) {
44419               if (timeout) {
44420                   clearTimeout(timeout);
44421                   timeout = null;
44422               }
44423               previous = now;
44424               result = fn.apply(storedContext, storedArgs);
44425               if (!timeout) {
44426                   storedContext = null;
44427                   storedArgs = [];
44428               }
44429           }
44430           else if (!timeout) {
44431               timeout = window.setTimeout(later, remaining);
44432           }
44433           return result;
44434       };
44435   }
44436   
44437 });
44438
44439  
44440
44441