930b9ceaafc1bb11b268e7fa3d6f98334c43c6af
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3951  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952  * @cfg {String} size (sm|lg|xl) default empty
3953  * @cfg {Number} max_width set the max width of modal
3954  * @cfg {Boolean} editableTitle can the title be edited
3955
3956  *
3957  *
3958  * @constructor
3959  * Create a new Modal Dialog
3960  * @param {Object} config The config object
3961  */
3962
3963 Roo.bootstrap.Modal = function(config){
3964     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3965     this.addEvents({
3966         // raw events
3967         /**
3968          * @event btnclick
3969          * The raw btnclick event for the button
3970          * @param {Roo.EventObject} e
3971          */
3972         "btnclick" : true,
3973         /**
3974          * @event resize
3975          * Fire when dialog resize
3976          * @param {Roo.bootstrap.Modal} this
3977          * @param {Roo.EventObject} e
3978          */
3979         "resize" : true,
3980         /**
3981          * @event titlechanged
3982          * Fire when the editable title has been changed
3983          * @param {Roo.bootstrap.Modal} this
3984          * @param {Roo.EventObject} value
3985          */
3986         "titlechanged" : true 
3987         
3988     });
3989     this.buttons = this.buttons || [];
3990
3991     if (this.tmpl) {
3992         this.tmpl = Roo.factory(this.tmpl);
3993     }
3994
3995 };
3996
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3998
3999     title : 'test dialog',
4000
4001     buttons : false,
4002
4003     // set on load...
4004
4005     html: false,
4006
4007     tmp: false,
4008
4009     specificTitle: false,
4010
4011     buttonPosition: 'right',
4012
4013     allow_close : true,
4014
4015     animate : true,
4016
4017     fitwindow: false,
4018     
4019      // private
4020     dialogEl: false,
4021     bodyEl:  false,
4022     footerEl:  false,
4023     titleEl:  false,
4024     closeEl:  false,
4025
4026     size: '',
4027     
4028     max_width: 0,
4029     
4030     max_height: 0,
4031     
4032     fit_content: false,
4033     editableTitle  : false,
4034
4035     onRender : function(ct, position)
4036     {
4037         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4038
4039         if(!this.el){
4040             var cfg = Roo.apply({},  this.getAutoCreate());
4041             cfg.id = Roo.id();
4042             //if(!cfg.name){
4043             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4044             //}
4045             //if (!cfg.name.length) {
4046             //    delete cfg.name;
4047            // }
4048             if (this.cls) {
4049                 cfg.cls += ' ' + this.cls;
4050             }
4051             if (this.style) {
4052                 cfg.style = this.style;
4053             }
4054             this.el = Roo.get(document.body).createChild(cfg, position);
4055         }
4056         //var type = this.el.dom.type;
4057
4058
4059         if(this.tabIndex !== undefined){
4060             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4061         }
4062
4063         this.dialogEl = this.el.select('.modal-dialog',true).first();
4064         this.bodyEl = this.el.select('.modal-body',true).first();
4065         this.closeEl = this.el.select('.modal-header .close', true).first();
4066         this.headerEl = this.el.select('.modal-header',true).first();
4067         this.titleEl = this.el.select('.modal-title',true).first();
4068         this.footerEl = this.el.select('.modal-footer',true).first();
4069
4070         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4071         
4072         //this.el.addClass("x-dlg-modal");
4073
4074         if (this.buttons.length) {
4075             Roo.each(this.buttons, function(bb) {
4076                 var b = Roo.apply({}, bb);
4077                 b.xns = b.xns || Roo.bootstrap;
4078                 b.xtype = b.xtype || 'Button';
4079                 if (typeof(b.listeners) == 'undefined') {
4080                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4081                 }
4082
4083                 var btn = Roo.factory(b);
4084
4085                 btn.render(this.getButtonContainer());
4086
4087             },this);
4088         }
4089         // render the children.
4090         var nitems = [];
4091
4092         if(typeof(this.items) != 'undefined'){
4093             var items = this.items;
4094             delete this.items;
4095
4096             for(var i =0;i < items.length;i++) {
4097                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4098             }
4099         }
4100
4101         this.items = nitems;
4102
4103         // where are these used - they used to be body/close/footer
4104
4105
4106         this.initEvents();
4107         //this.el.addClass([this.fieldClass, this.cls]);
4108
4109     },
4110
4111     getAutoCreate : function()
4112     {
4113         // we will default to modal-body-overflow - might need to remove or make optional later.
4114         var bdy = {
4115                 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''), 
4116                 html : this.html || ''
4117         };
4118
4119         var title = {
4120             tag: 'h4',
4121             cls : 'modal-title',
4122             html : this.title
4123         };
4124
4125         if(this.specificTitle){ // WTF is this?
4126             title = this.title;
4127         }
4128
4129         var header = [];
4130         if (this.allow_close && Roo.bootstrap.version == 3) {
4131             header.push({
4132                 tag: 'button',
4133                 cls : 'close',
4134                 html : '&times'
4135             });
4136         }
4137
4138         header.push(title);
4139
4140         if (this.editableTitle) {
4141             header.push({
4142                 cls: 'form-control roo-editable-title d-none',
4143                 tag: 'input',
4144                 type: 'text'
4145             });
4146         }
4147         
4148         if (this.allow_close && Roo.bootstrap.version == 4) {
4149             header.push({
4150                 tag: 'button',
4151                 cls : 'close',
4152                 html : '&times'
4153             });
4154         }
4155         
4156         var size = '';
4157
4158         if(this.size.length){
4159             size = 'modal-' + this.size;
4160         }
4161         
4162         var footer = Roo.bootstrap.version == 3 ?
4163             {
4164                 cls : 'modal-footer',
4165                 cn : [
4166                     {
4167                         tag: 'div',
4168                         cls: 'btn-' + this.buttonPosition
4169                     }
4170                 ]
4171
4172             } :
4173             {  // BS4 uses mr-auto on left buttons....
4174                 cls : 'modal-footer'
4175             };
4176
4177             
4178
4179         
4180         
4181         var modal = {
4182             cls: "modal",
4183              cn : [
4184                 {
4185                     cls: "modal-dialog " + size,
4186                     cn : [
4187                         {
4188                             cls : "modal-content",
4189                             cn : [
4190                                 {
4191                                     cls : 'modal-header',
4192                                     cn : header
4193                                 },
4194                                 bdy,
4195                                 footer
4196                             ]
4197
4198                         }
4199                     ]
4200
4201                 }
4202             ]
4203         };
4204
4205         if(this.animate){
4206             modal.cls += ' fade';
4207         }
4208
4209         return modal;
4210
4211     },
4212     getChildContainer : function() {
4213
4214          return this.bodyEl;
4215
4216     },
4217     getButtonContainer : function() {
4218         
4219          return Roo.bootstrap.version == 4 ?
4220             this.el.select('.modal-footer',true).first()
4221             : this.el.select('.modal-footer div',true).first();
4222
4223     },
4224     initEvents : function()
4225     {
4226         if (this.allow_close) {
4227             this.closeEl.on('click', this.hide, this);
4228         }
4229         Roo.EventManager.onWindowResize(this.resize, this, true);
4230         if (this.editableTitle) {
4231             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4232             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233             this.headerEditEl.on('keyup', function(e) {
4234                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235                         this.toggleHeaderInput(false)
4236                     }
4237                 }, this);
4238             this.headerEditEl.on('blur', function(e) {
4239                 this.toggleHeaderInput(false)
4240             },this);
4241         }
4242
4243     },
4244   
4245
4246     resize : function()
4247     {
4248         this.maskEl.setSize(
4249             Roo.lib.Dom.getViewWidth(true),
4250             Roo.lib.Dom.getViewHeight(true)
4251         );
4252         
4253         if (this.fitwindow) {
4254             
4255            
4256             this.setSize(
4257                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4259             );
4260             return;
4261         }
4262         
4263         if(this.max_width !== 0) {
4264             
4265             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4266             
4267             if(this.height) {
4268                 this.setSize(w, this.height);
4269                 return;
4270             }
4271             
4272             if(this.max_height) {
4273                 this.setSize(w,Math.min(
4274                     this.max_height,
4275                     Roo.lib.Dom.getViewportHeight(true) - 60
4276                 ));
4277                 
4278                 return;
4279             }
4280             
4281             if(!this.fit_content) {
4282                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4283                 return;
4284             }
4285             
4286             this.setSize(w, Math.min(
4287                 60 +
4288                 this.headerEl.getHeight() + 
4289                 this.footerEl.getHeight() + 
4290                 this.getChildHeight(this.bodyEl.dom.childNodes),
4291                 Roo.lib.Dom.getViewportHeight(true) - 60)
4292             );
4293         }
4294         
4295     },
4296
4297     setSize : function(w,h)
4298     {
4299         if (!w && !h) {
4300             return;
4301         }
4302         
4303         this.resizeTo(w,h);
4304     },
4305
4306     show : function() {
4307
4308         if (!this.rendered) {
4309             this.render();
4310         }
4311         this.toggleHeaderInput(false);
4312         //this.el.setStyle('display', 'block');
4313         this.el.removeClass('hideing');
4314         this.el.dom.style.display='block';
4315         
4316         Roo.get(document.body).addClass('modal-open');
4317  
4318         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4319             
4320             (function(){
4321                 this.el.addClass('show');
4322                 this.el.addClass('in');
4323             }).defer(50, this);
4324         }else{
4325             this.el.addClass('show');
4326             this.el.addClass('in');
4327         }
4328
4329         // not sure how we can show data in here..
4330         //if (this.tmpl) {
4331         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4332         //}
4333
4334         Roo.get(document.body).addClass("x-body-masked");
4335         
4336         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4337         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338         this.maskEl.dom.style.display = 'block';
4339         this.maskEl.addClass('show');
4340         
4341         
4342         this.resize();
4343         
4344         this.fireEvent('show', this);
4345
4346         // set zindex here - otherwise it appears to be ignored...
4347         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4348
4349         (function () {
4350             this.items.forEach( function(e) {
4351                 e.layout ? e.layout() : false;
4352
4353             });
4354         }).defer(100,this);
4355
4356     },
4357     hide : function()
4358     {
4359         if(this.fireEvent("beforehide", this) !== false){
4360             
4361             this.maskEl.removeClass('show');
4362             
4363             this.maskEl.dom.style.display = '';
4364             Roo.get(document.body).removeClass("x-body-masked");
4365             this.el.removeClass('in');
4366             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4367
4368             if(this.animate){ // why
4369                 this.el.addClass('hideing');
4370                 this.el.removeClass('show');
4371                 (function(){
4372                     if (!this.el.hasClass('hideing')) {
4373                         return; // it's been shown again...
4374                     }
4375                     
4376                     this.el.dom.style.display='';
4377
4378                     Roo.get(document.body).removeClass('modal-open');
4379                     this.el.removeClass('hideing');
4380                 }).defer(150,this);
4381                 
4382             }else{
4383                 this.el.removeClass('show');
4384                 this.el.dom.style.display='';
4385                 Roo.get(document.body).removeClass('modal-open');
4386
4387             }
4388             this.fireEvent('hide', this);
4389         }
4390     },
4391     isVisible : function()
4392     {
4393         
4394         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4395         
4396     },
4397
4398     addButton : function(str, cb)
4399     {
4400
4401
4402         var b = Roo.apply({}, { html : str } );
4403         b.xns = b.xns || Roo.bootstrap;
4404         b.xtype = b.xtype || 'Button';
4405         if (typeof(b.listeners) == 'undefined') {
4406             b.listeners = { click : cb.createDelegate(this)  };
4407         }
4408
4409         var btn = Roo.factory(b);
4410
4411         btn.render(this.getButtonContainer());
4412
4413         return btn;
4414
4415     },
4416
4417     setDefaultButton : function(btn)
4418     {
4419         //this.el.select('.modal-footer').()
4420     },
4421
4422     resizeTo: function(w,h)
4423     {
4424         this.dialogEl.setWidth(w);
4425         
4426         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4427
4428         this.bodyEl.setHeight(h - diff);
4429         
4430         this.fireEvent('resize', this);
4431     },
4432     
4433     setContentSize  : function(w, h)
4434     {
4435
4436     },
4437     onButtonClick: function(btn,e)
4438     {
4439         //Roo.log([a,b,c]);
4440         this.fireEvent('btnclick', btn.name, e);
4441     },
4442      /**
4443      * Set the title of the Dialog
4444      * @param {String} str new Title
4445      */
4446     setTitle: function(str) {
4447         this.titleEl.dom.innerHTML = str;
4448         this.title = str;
4449     },
4450     /**
4451      * Set the body of the Dialog
4452      * @param {String} str new Title
4453      */
4454     setBody: function(str) {
4455         this.bodyEl.dom.innerHTML = str;
4456     },
4457     /**
4458      * Set the body of the Dialog using the template
4459      * @param {Obj} data - apply this data to the template and replace the body contents.
4460      */
4461     applyBody: function(obj)
4462     {
4463         if (!this.tmpl) {
4464             Roo.log("Error - using apply Body without a template");
4465             //code
4466         }
4467         this.tmpl.overwrite(this.bodyEl, obj);
4468     },
4469     
4470     getChildHeight : function(child_nodes)
4471     {
4472         if(
4473             !child_nodes ||
4474             child_nodes.length == 0
4475         ) {
4476             return 0;
4477         }
4478         
4479         var child_height = 0;
4480         
4481         for(var i = 0; i < child_nodes.length; i++) {
4482             
4483             /*
4484             * for modal with tabs...
4485             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4486                 
4487                 var layout_childs = child_nodes[i].childNodes;
4488                 
4489                 for(var j = 0; j < layout_childs.length; j++) {
4490                     
4491                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4492                         
4493                         var layout_body_childs = layout_childs[j].childNodes;
4494                         
4495                         for(var k = 0; k < layout_body_childs.length; k++) {
4496                             
4497                             if(layout_body_childs[k].classList.contains('navbar')) {
4498                                 child_height += layout_body_childs[k].offsetHeight;
4499                                 continue;
4500                             }
4501                             
4502                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4503                                 
4504                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4505                                 
4506                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4507                                     
4508                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4510                                         continue;
4511                                     }
4512                                     
4513                                 }
4514                                 
4515                             }
4516                             
4517                         }
4518                     }
4519                 }
4520                 continue;
4521             }
4522             */
4523             
4524             child_height += child_nodes[i].offsetHeight;
4525             // Roo.log(child_nodes[i].offsetHeight);
4526         }
4527         
4528         return child_height;
4529     },
4530     toggleHeaderInput : function(is_edit)
4531     {
4532         if (!this.editableTitle) {
4533             return; // not editable.
4534         }
4535         if (is_edit && this.is_header_editing) {
4536             return; // already editing..
4537         }
4538         if (is_edit) {
4539     
4540             this.headerEditEl.dom.value = this.title;
4541             this.headerEditEl.removeClass('d-none');
4542             this.headerEditEl.dom.focus();
4543             this.titleEl.addClass('d-none');
4544             
4545             this.is_header_editing = true;
4546             return
4547         }
4548         // flip back to not editing.
4549         this.title = this.headerEditEl.dom.value;
4550         this.headerEditEl.addClass('d-none');
4551         this.titleEl.removeClass('d-none');
4552         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553         this.is_header_editing = false;
4554         this.fireEvent('titlechanged', this, this.title);
4555     
4556             
4557         
4558     }
4559
4560 });
4561
4562
4563 Roo.apply(Roo.bootstrap.Modal,  {
4564     /**
4565          * Button config that displays a single OK button
4566          * @type Object
4567          */
4568         OK :  [{
4569             name : 'ok',
4570             weight : 'primary',
4571             html : 'OK'
4572         }],
4573         /**
4574          * Button config that displays Yes and No buttons
4575          * @type Object
4576          */
4577         YESNO : [
4578             {
4579                 name  : 'no',
4580                 html : 'No'
4581             },
4582             {
4583                 name  :'yes',
4584                 weight : 'primary',
4585                 html : 'Yes'
4586             }
4587         ],
4588
4589         /**
4590          * Button config that displays OK and Cancel buttons
4591          * @type Object
4592          */
4593         OKCANCEL : [
4594             {
4595                name : 'cancel',
4596                 html : 'Cancel'
4597             },
4598             {
4599                 name : 'ok',
4600                 weight : 'primary',
4601                 html : 'OK'
4602             }
4603         ],
4604         /**
4605          * Button config that displays Yes, No and Cancel buttons
4606          * @type Object
4607          */
4608         YESNOCANCEL : [
4609             {
4610                 name : 'yes',
4611                 weight : 'primary',
4612                 html : 'Yes'
4613             },
4614             {
4615                 name : 'no',
4616                 html : 'No'
4617             },
4618             {
4619                 name : 'cancel',
4620                 html : 'Cancel'
4621             }
4622         ],
4623         
4624         zIndex : 10001
4625 });
4626
4627 /*
4628  * - LGPL
4629  *
4630  * messagebox - can be used as a replace
4631  * 
4632  */
4633 /**
4634  * @class Roo.MessageBox
4635  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4636  * Example usage:
4637  *<pre><code>
4638 // Basic alert:
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4640
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4643     if (btn == 'ok'){
4644         // process text value...
4645     }
4646 });
4647
4648 // Show a dialog using config options:
4649 Roo.Msg.show({
4650    title:'Save Changes?',
4651    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652    buttons: Roo.Msg.YESNOCANCEL,
4653    fn: processResult,
4654    animEl: 'elId'
4655 });
4656 </code></pre>
4657  * @singleton
4658  */
4659 Roo.bootstrap.MessageBox = function(){
4660     var dlg, opt, mask, waitTimer;
4661     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662     var buttons, activeTextEl, bwidth;
4663
4664     
4665     // private
4666     var handleButton = function(button){
4667         dlg.hide();
4668         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4669     };
4670
4671     // private
4672     var handleHide = function(){
4673         if(opt && opt.cls){
4674             dlg.el.removeClass(opt.cls);
4675         }
4676         //if(waitTimer){
4677         //    Roo.TaskMgr.stop(waitTimer);
4678         //    waitTimer = null;
4679         //}
4680     };
4681
4682     // private
4683     var updateButtons = function(b){
4684         var width = 0;
4685         if(!b){
4686             buttons["ok"].hide();
4687             buttons["cancel"].hide();
4688             buttons["yes"].hide();
4689             buttons["no"].hide();
4690             dlg.footerEl.hide();
4691             
4692             return width;
4693         }
4694         dlg.footerEl.show();
4695         for(var k in buttons){
4696             if(typeof buttons[k] != "function"){
4697                 if(b[k]){
4698                     buttons[k].show();
4699                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700                     width += buttons[k].el.getWidth()+15;
4701                 }else{
4702                     buttons[k].hide();
4703                 }
4704             }
4705         }
4706         return width;
4707     };
4708
4709     // private
4710     var handleEsc = function(d, k, e){
4711         if(opt && opt.closable !== false){
4712             dlg.hide();
4713         }
4714         if(e){
4715             e.stopEvent();
4716         }
4717     };
4718
4719     return {
4720         /**
4721          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722          * @return {Roo.BasicDialog} The BasicDialog element
4723          */
4724         getDialog : function(){
4725            if(!dlg){
4726                 dlg = new Roo.bootstrap.Modal( {
4727                     //draggable: true,
4728                     //resizable:false,
4729                     //constraintoviewport:false,
4730                     //fixedcenter:true,
4731                     //collapsible : false,
4732                     //shim:true,
4733                     //modal: true,
4734                 //    width: 'auto',
4735                   //  height:100,
4736                     //buttonAlign:"center",
4737                     closeClick : function(){
4738                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4739                             handleButton("no");
4740                         }else{
4741                             handleButton("cancel");
4742                         }
4743                     }
4744                 });
4745                 dlg.render();
4746                 dlg.on("hide", handleHide);
4747                 mask = dlg.mask;
4748                 //dlg.addKeyListener(27, handleEsc);
4749                 buttons = {};
4750                 this.buttons = buttons;
4751                 var bt = this.buttonText;
4752                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4756                 //Roo.log(buttons);
4757                 bodyEl = dlg.bodyEl.createChild({
4758
4759                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760                         '<textarea class="roo-mb-textarea"></textarea>' +
4761                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4762                 });
4763                 msgEl = bodyEl.dom.firstChild;
4764                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765                 textboxEl.enableDisplayMode();
4766                 textboxEl.addKeyListener([10,13], function(){
4767                     if(dlg.isVisible() && opt && opt.buttons){
4768                         if(opt.buttons.ok){
4769                             handleButton("ok");
4770                         }else if(opt.buttons.yes){
4771                             handleButton("yes");
4772                         }
4773                     }
4774                 });
4775                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776                 textareaEl.enableDisplayMode();
4777                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778                 progressEl.enableDisplayMode();
4779                 
4780                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781                 var pf = progressEl.dom.firstChild;
4782                 if (pf) {
4783                     pp = Roo.get(pf.firstChild);
4784                     pp.setHeight(pf.offsetHeight);
4785                 }
4786                 
4787             }
4788             return dlg;
4789         },
4790
4791         /**
4792          * Updates the message box body text
4793          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794          * the XHTML-compliant non-breaking space character '&amp;#160;')
4795          * @return {Roo.MessageBox} This message box
4796          */
4797         updateText : function(text)
4798         {
4799             if(!dlg.isVisible() && !opt.width){
4800                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4802             }
4803             msgEl.innerHTML = text || '&#160;';
4804       
4805             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4807             var w = Math.max(
4808                     Math.min(opt.width || cw , this.maxWidth), 
4809                     Math.max(opt.minWidth || this.minWidth, bwidth)
4810             );
4811             if(opt.prompt){
4812                 activeTextEl.setWidth(w);
4813             }
4814             if(dlg.isVisible()){
4815                 dlg.fixedcenter = false;
4816             }
4817             // to big, make it scroll. = But as usual stupid IE does not support
4818             // !important..
4819             
4820             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4823             } else {
4824                 bodyEl.dom.style.height = '';
4825                 bodyEl.dom.style.overflowY = '';
4826             }
4827             if (cw > w) {
4828                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4829             } else {
4830                 bodyEl.dom.style.overflowX = '';
4831             }
4832             
4833             dlg.setContentSize(w, bodyEl.getHeight());
4834             if(dlg.isVisible()){
4835                 dlg.fixedcenter = true;
4836             }
4837             return this;
4838         },
4839
4840         /**
4841          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4842          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845          * @return {Roo.MessageBox} This message box
4846          */
4847         updateProgress : function(value, text){
4848             if(text){
4849                 this.updateText(text);
4850             }
4851             
4852             if (pp) { // weird bug on my firefox - for some reason this is not defined
4853                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855             }
4856             return this;
4857         },        
4858
4859         /**
4860          * Returns true if the message box is currently displayed
4861          * @return {Boolean} True if the message box is visible, else false
4862          */
4863         isVisible : function(){
4864             return dlg && dlg.isVisible();  
4865         },
4866
4867         /**
4868          * Hides the message box if it is displayed
4869          */
4870         hide : function(){
4871             if(this.isVisible()){
4872                 dlg.hide();
4873             }  
4874         },
4875
4876         /**
4877          * Displays a new message box, or reinitializes an existing message box, based on the config options
4878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879          * The following config object properties are supported:
4880          * <pre>
4881 Property    Type             Description
4882 ----------  ---------------  ------------------------------------------------------------------------------------
4883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4884                                    closes (defaults to undefined)
4885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4888                                    progress and wait dialogs will ignore this property and always hide the
4889                                    close button as they can only be closed programmatically.
4890 cls               String           A custom CSS class to apply to the message box element
4891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4892                                    displayed (defaults to 75)
4893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4894                                    function will be btn (the name of the button that was clicked, if applicable,
4895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4896                                    Progress and wait dialogs will ignore this option since they do not respond to
4897                                    user actions and can only be closed programmatically, so any required function
4898                                    should be called by the same code after it closes the dialog.
4899 icon              String           A CSS class that provides a background image to be used as an icon for
4900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4903 modal             Boolean          False to allow user interaction with the page while the message box is
4904                                    displayed (defaults to true)
4905 msg               String           A string that will replace the existing message box body text (defaults
4906                                    to the XHTML-compliant non-breaking space character '&#160;')
4907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4908 progress          Boolean          True to display a progress bar (defaults to false)
4909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4912 title             String           The title text
4913 value             String           The string value to set into the active textbox element if displayed
4914 wait              Boolean          True to display a progress bar (defaults to false)
4915 width             Number           The width of the dialog in pixels
4916 </pre>
4917          *
4918          * Example usage:
4919          * <pre><code>
4920 Roo.Msg.show({
4921    title: 'Address',
4922    msg: 'Please enter your address:',
4923    width: 300,
4924    buttons: Roo.MessageBox.OKCANCEL,
4925    multiline: true,
4926    fn: saveAddress,
4927    animEl: 'addAddressBtn'
4928 });
4929 </code></pre>
4930          * @param {Object} config Configuration options
4931          * @return {Roo.MessageBox} This message box
4932          */
4933         show : function(options)
4934         {
4935             
4936             // this causes nightmares if you show one dialog after another
4937             // especially on callbacks..
4938              
4939             if(this.isVisible()){
4940                 
4941                 this.hide();
4942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4944                 Roo.log("New Dialog Message:" +  options.msg )
4945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4947                 
4948             }
4949             var d = this.getDialog();
4950             opt = options;
4951             d.setTitle(opt.title || "&#160;");
4952             d.closeEl.setDisplayed(opt.closable !== false);
4953             activeTextEl = textboxEl;
4954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4955             if(opt.prompt){
4956                 if(opt.multiline){
4957                     textboxEl.hide();
4958                     textareaEl.show();
4959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4960                         opt.multiline : this.defaultTextHeight);
4961                     activeTextEl = textareaEl;
4962                 }else{
4963                     textboxEl.show();
4964                     textareaEl.hide();
4965                 }
4966             }else{
4967                 textboxEl.hide();
4968                 textareaEl.hide();
4969             }
4970             progressEl.setDisplayed(opt.progress === true);
4971             if (opt.progress) {
4972                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4973             }
4974             this.updateProgress(0);
4975             activeTextEl.dom.value = opt.value || "";
4976             if(opt.prompt){
4977                 dlg.setDefaultButton(activeTextEl);
4978             }else{
4979                 var bs = opt.buttons;
4980                 var db = null;
4981                 if(bs && bs.ok){
4982                     db = buttons["ok"];
4983                 }else if(bs && bs.yes){
4984                     db = buttons["yes"];
4985                 }
4986                 dlg.setDefaultButton(db);
4987             }
4988             bwidth = updateButtons(opt.buttons);
4989             this.updateText(opt.msg);
4990             if(opt.cls){
4991                 d.el.addClass(opt.cls);
4992             }
4993             d.proxyDrag = opt.proxyDrag === true;
4994             d.modal = opt.modal !== false;
4995             d.mask = opt.modal !== false ? mask : false;
4996             if(!d.isVisible()){
4997                 // force it to the end of the z-index stack so it gets a cursor in FF
4998                 document.body.appendChild(dlg.el.dom);
4999                 d.animateTarget = null;
5000                 d.show(options.animEl);
5001             }
5002             return this;
5003         },
5004
5005         /**
5006          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5007          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008          * and closing the message box when the process is complete.
5009          * @param {String} title The title bar text
5010          * @param {String} msg The message box body text
5011          * @return {Roo.MessageBox} This message box
5012          */
5013         progress : function(title, msg){
5014             this.show({
5015                 title : title,
5016                 msg : msg,
5017                 buttons: false,
5018                 progress:true,
5019                 closable:false,
5020                 minWidth: this.minProgressWidth,
5021                 modal : true
5022             });
5023             return this;
5024         },
5025
5026         /**
5027          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028          * If a callback function is passed it will be called after the user clicks the button, and the
5029          * id of the button that was clicked will be passed as the only parameter to the callback
5030          * (could also be the top-right close button).
5031          * @param {String} title The title bar text
5032          * @param {String} msg The message box body text
5033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034          * @param {Object} scope (optional) The scope of the callback function
5035          * @return {Roo.MessageBox} This message box
5036          */
5037         alert : function(title, msg, fn, scope)
5038         {
5039             this.show({
5040                 title : title,
5041                 msg : msg,
5042                 buttons: this.OK,
5043                 fn: fn,
5044                 closable : false,
5045                 scope : scope,
5046                 modal : true
5047             });
5048             return this;
5049         },
5050
5051         /**
5052          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5053          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054          * You are responsible for closing the message box when the process is complete.
5055          * @param {String} msg The message box body text
5056          * @param {String} title (optional) The title bar text
5057          * @return {Roo.MessageBox} This message box
5058          */
5059         wait : function(msg, title){
5060             this.show({
5061                 title : title,
5062                 msg : msg,
5063                 buttons: false,
5064                 closable:false,
5065                 progress:true,
5066                 modal:true,
5067                 width:300,
5068                 wait:true
5069             });
5070             waitTimer = Roo.TaskMgr.start({
5071                 run: function(i){
5072                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5073                 },
5074                 interval: 1000
5075             });
5076             return this;
5077         },
5078
5079         /**
5080          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083          * @param {String} title The title bar text
5084          * @param {String} msg The message box body text
5085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086          * @param {Object} scope (optional) The scope of the callback function
5087          * @return {Roo.MessageBox} This message box
5088          */
5089         confirm : function(title, msg, fn, scope){
5090             this.show({
5091                 title : title,
5092                 msg : msg,
5093                 buttons: this.YESNO,
5094                 fn: fn,
5095                 scope : scope,
5096                 modal : true
5097             });
5098             return this;
5099         },
5100
5101         /**
5102          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5104          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105          * (could also be the top-right close button) and the text that was entered will be passed as the two
5106          * parameters to the callback.
5107          * @param {String} title The title bar text
5108          * @param {String} msg The message box body text
5109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110          * @param {Object} scope (optional) The scope of the callback function
5111          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         prompt : function(title, msg, fn, scope, multiline){
5116             this.show({
5117                 title : title,
5118                 msg : msg,
5119                 buttons: this.OKCANCEL,
5120                 fn: fn,
5121                 minWidth:250,
5122                 scope : scope,
5123                 prompt:true,
5124                 multiline: multiline,
5125                 modal : true
5126             });
5127             return this;
5128         },
5129
5130         /**
5131          * Button config that displays a single OK button
5132          * @type Object
5133          */
5134         OK : {ok:true},
5135         /**
5136          * Button config that displays Yes and No buttons
5137          * @type Object
5138          */
5139         YESNO : {yes:true, no:true},
5140         /**
5141          * Button config that displays OK and Cancel buttons
5142          * @type Object
5143          */
5144         OKCANCEL : {ok:true, cancel:true},
5145         /**
5146          * Button config that displays Yes, No and Cancel buttons
5147          * @type Object
5148          */
5149         YESNOCANCEL : {yes:true, no:true, cancel:true},
5150
5151         /**
5152          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5153          * @type Number
5154          */
5155         defaultTextHeight : 75,
5156         /**
5157          * The maximum width in pixels of the message box (defaults to 600)
5158          * @type Number
5159          */
5160         maxWidth : 600,
5161         /**
5162          * The minimum width in pixels of the message box (defaults to 100)
5163          * @type Number
5164          */
5165         minWidth : 100,
5166         /**
5167          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5168          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5169          * @type Number
5170          */
5171         minProgressWidth : 250,
5172         /**
5173          * An object containing the default button text strings that can be overriden for localized language support.
5174          * Supported properties are: ok, cancel, yes and no.
5175          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5176          * @type Object
5177          */
5178         buttonText : {
5179             ok : "OK",
5180             cancel : "Cancel",
5181             yes : "Yes",
5182             no : "No"
5183         }
5184     };
5185 }();
5186
5187 /**
5188  * Shorthand for {@link Roo.MessageBox}
5189  */
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5192 /*
5193  * - LGPL
5194  *
5195  * navbar
5196  * 
5197  */
5198
5199 /**
5200  * @class Roo.bootstrap.Navbar
5201  * @extends Roo.bootstrap.Component
5202  * Bootstrap Navbar class
5203
5204  * @constructor
5205  * Create a new Navbar
5206  * @param {Object} config The config object
5207  */
5208
5209
5210 Roo.bootstrap.Navbar = function(config){
5211     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5212     this.addEvents({
5213         // raw events
5214         /**
5215          * @event beforetoggle
5216          * Fire before toggle the menu
5217          * @param {Roo.EventObject} e
5218          */
5219         "beforetoggle" : true
5220     });
5221 };
5222
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5224     
5225     
5226    
5227     // private
5228     navItems : false,
5229     loadMask : false,
5230     
5231     
5232     getAutoCreate : function(){
5233         
5234         
5235         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5236         
5237     },
5238     
5239     initEvents :function ()
5240     {
5241         //Roo.log(this.el.select('.navbar-toggle',true));
5242         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5243         
5244         var mark = {
5245             tag: "div",
5246             cls:"x-dlg-mask"
5247         };
5248         
5249         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5250         
5251         var size = this.el.getSize();
5252         this.maskEl.setSize(size.width, size.height);
5253         this.maskEl.enableDisplayMode("block");
5254         this.maskEl.hide();
5255         
5256         if(this.loadMask){
5257             this.maskEl.show();
5258         }
5259     },
5260     
5261     
5262     getChildContainer : function()
5263     {
5264         if (this.el && this.el.select('.collapse').getCount()) {
5265             return this.el.select('.collapse',true).first();
5266         }
5267         
5268         return this.el;
5269     },
5270     
5271     mask : function()
5272     {
5273         this.maskEl.show();
5274     },
5275     
5276     unmask : function()
5277     {
5278         this.maskEl.hide();
5279     },
5280     onToggle : function()
5281     {
5282         
5283         if(this.fireEvent('beforetoggle', this) === false){
5284             return;
5285         }
5286         var ce = this.el.select('.navbar-collapse',true).first();
5287       
5288         if (!ce.hasClass('show')) {
5289            this.expand();
5290         } else {
5291             this.collapse();
5292         }
5293         
5294         
5295     
5296     },
5297     /**
5298      * Expand the navbar pulldown 
5299      */
5300     expand : function ()
5301     {
5302        
5303         var ce = this.el.select('.navbar-collapse',true).first();
5304         if (ce.hasClass('collapsing')) {
5305             return;
5306         }
5307         ce.dom.style.height = '';
5308                // show it...
5309         ce.addClass('in'); // old...
5310         ce.removeClass('collapse');
5311         ce.addClass('show');
5312         var h = ce.getHeight();
5313         Roo.log(h);
5314         ce.removeClass('show');
5315         // at this point we should be able to see it..
5316         ce.addClass('collapsing');
5317         
5318         ce.setHeight(0); // resize it ...
5319         ce.on('transitionend', function() {
5320             //Roo.log('done transition');
5321             ce.removeClass('collapsing');
5322             ce.addClass('show');
5323             ce.removeClass('collapse');
5324
5325             ce.dom.style.height = '';
5326         }, this, { single: true} );
5327         ce.setHeight(h);
5328         ce.dom.scrollTop = 0;
5329     },
5330     /**
5331      * Collapse the navbar pulldown 
5332      */
5333     collapse : function()
5334     {
5335          var ce = this.el.select('.navbar-collapse',true).first();
5336        
5337         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338             // it's collapsed or collapsing..
5339             return;
5340         }
5341         ce.removeClass('in'); // old...
5342         ce.setHeight(ce.getHeight());
5343         ce.removeClass('show');
5344         ce.addClass('collapsing');
5345         
5346         ce.on('transitionend', function() {
5347             ce.dom.style.height = '';
5348             ce.removeClass('collapsing');
5349             ce.addClass('collapse');
5350         }, this, { single: true} );
5351         ce.setHeight(0);
5352     }
5353     
5354     
5355     
5356 });
5357
5358
5359
5360  
5361
5362  /*
5363  * - LGPL
5364  *
5365  * navbar
5366  * 
5367  */
5368
5369 /**
5370  * @class Roo.bootstrap.NavSimplebar
5371  * @extends Roo.bootstrap.Navbar
5372  * Bootstrap Sidebar class
5373  *
5374  * @cfg {Boolean} inverse is inverted color
5375  * 
5376  * @cfg {String} type (nav | pills | tabs)
5377  * @cfg {Boolean} arrangement stacked | justified
5378  * @cfg {String} align (left | right) alignment
5379  * 
5380  * @cfg {Boolean} main (true|false) main nav bar? default false
5381  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5382  * 
5383  * @cfg {String} tag (header|footer|nav|div) default is nav 
5384
5385  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5386  * 
5387  * 
5388  * @constructor
5389  * Create a new Sidebar
5390  * @param {Object} config The config object
5391  */
5392
5393
5394 Roo.bootstrap.NavSimplebar = function(config){
5395     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5396 };
5397
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5399     
5400     inverse: false,
5401     
5402     type: false,
5403     arrangement: '',
5404     align : false,
5405     
5406     weight : 'light',
5407     
5408     main : false,
5409     
5410     
5411     tag : false,
5412     
5413     
5414     getAutoCreate : function(){
5415         
5416         
5417         var cfg = {
5418             tag : this.tag || 'div',
5419             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5420         };
5421         if (['light','white'].indexOf(this.weight) > -1) {
5422             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5423         }
5424         cfg.cls += ' bg-' + this.weight;
5425         
5426         if (this.inverse) {
5427             cfg.cls += ' navbar-inverse';
5428             
5429         }
5430         
5431         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5432         
5433         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5434             return cfg;
5435         }
5436         
5437         
5438     
5439         
5440         cfg.cn = [
5441             {
5442                 cls: 'nav nav-' + this.xtype,
5443                 tag : 'ul'
5444             }
5445         ];
5446         
5447          
5448         this.type = this.type || 'nav';
5449         if (['tabs','pills'].indexOf(this.type) != -1) {
5450             cfg.cn[0].cls += ' nav-' + this.type
5451         
5452         
5453         } else {
5454             if (this.type!=='nav') {
5455                 Roo.log('nav type must be nav/tabs/pills')
5456             }
5457             cfg.cn[0].cls += ' navbar-nav'
5458         }
5459         
5460         
5461         
5462         
5463         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464             cfg.cn[0].cls += ' nav-' + this.arrangement;
5465         }
5466         
5467         
5468         if (this.align === 'right') {
5469             cfg.cn[0].cls += ' navbar-right';
5470         }
5471         
5472         
5473         
5474         
5475         return cfg;
5476     
5477         
5478     }
5479     
5480     
5481     
5482 });
5483
5484
5485
5486  
5487
5488  
5489        /*
5490  * - LGPL
5491  *
5492  * navbar
5493  * navbar-fixed-top
5494  * navbar-expand-md  fixed-top 
5495  */
5496
5497 /**
5498  * @class Roo.bootstrap.NavHeaderbar
5499  * @extends Roo.bootstrap.NavSimplebar
5500  * Bootstrap Sidebar class
5501  *
5502  * @cfg {String} brand what is brand
5503  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504  * @cfg {String} brand_href href of the brand
5505  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5506  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5509  * 
5510  * @constructor
5511  * Create a new Sidebar
5512  * @param {Object} config The config object
5513  */
5514
5515
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5518       
5519 };
5520
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5522     
5523     position: '',
5524     brand: '',
5525     brand_href: false,
5526     srButton : true,
5527     autohide : false,
5528     desktopCenter : false,
5529    
5530     
5531     getAutoCreate : function(){
5532         
5533         var   cfg = {
5534             tag: this.nav || 'nav',
5535             cls: 'navbar navbar-expand-md',
5536             role: 'navigation',
5537             cn: []
5538         };
5539         
5540         var cn = cfg.cn;
5541         if (this.desktopCenter) {
5542             cn.push({cls : 'container', cn : []});
5543             cn = cn[0].cn;
5544         }
5545         
5546         if(this.srButton){
5547             var btn = {
5548                 tag: 'button',
5549                 type: 'button',
5550                 cls: 'navbar-toggle navbar-toggler',
5551                 'data-toggle': 'collapse',
5552                 cn: [
5553                     {
5554                         tag: 'span',
5555                         cls: 'sr-only',
5556                         html: 'Toggle navigation'
5557                     },
5558                     {
5559                         tag: 'span',
5560                         cls: 'icon-bar navbar-toggler-icon'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     }
5570                 ]
5571             };
5572             
5573             cn.push( Roo.bootstrap.version == 4 ? btn : {
5574                 tag: 'div',
5575                 cls: 'navbar-header',
5576                 cn: [
5577                     btn
5578                 ]
5579             });
5580         }
5581         
5582         cn.push({
5583             tag: 'div',
5584             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5585             cn : []
5586         });
5587         
5588         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5589         
5590         if (['light','white'].indexOf(this.weight) > -1) {
5591             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5592         }
5593         cfg.cls += ' bg-' + this.weight;
5594         
5595         
5596         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5598             
5599             // tag can override this..
5600             
5601             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5602         }
5603         
5604         if (this.brand !== '') {
5605             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5607                 tag: 'a',
5608                 href: this.brand_href ? this.brand_href : '#',
5609                 cls: 'navbar-brand',
5610                 cn: [
5611                 this.brand
5612                 ]
5613             });
5614         }
5615         
5616         if(this.main){
5617             cfg.cls += ' main-nav';
5618         }
5619         
5620         
5621         return cfg;
5622
5623         
5624     },
5625     getHeaderChildContainer : function()
5626     {
5627         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628             return this.el.select('.navbar-header',true).first();
5629         }
5630         
5631         return this.getChildContainer();
5632     },
5633     
5634     getChildContainer : function()
5635     {
5636          
5637         return this.el.select('.roo-navbar-collapse',true).first();
5638          
5639         
5640     },
5641     
5642     initEvents : function()
5643     {
5644         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5645         
5646         if (this.autohide) {
5647             
5648             var prevScroll = 0;
5649             var ft = this.el;
5650             
5651             Roo.get(document).on('scroll',function(e) {
5652                 var ns = Roo.get(document).getScroll().top;
5653                 var os = prevScroll;
5654                 prevScroll = ns;
5655                 
5656                 if(ns > os){
5657                     ft.removeClass('slideDown');
5658                     ft.addClass('slideUp');
5659                     return;
5660                 }
5661                 ft.removeClass('slideUp');
5662                 ft.addClass('slideDown');
5663                  
5664               
5665           },this);
5666         }
5667     }    
5668     
5669 });
5670
5671
5672
5673  
5674
5675  /*
5676  * - LGPL
5677  *
5678  * navbar
5679  * 
5680  */
5681
5682 /**
5683  * @class Roo.bootstrap.NavSidebar
5684  * @extends Roo.bootstrap.Navbar
5685  * Bootstrap Sidebar class
5686  * 
5687  * @constructor
5688  * Create a new Sidebar
5689  * @param {Object} config The config object
5690  */
5691
5692
5693 Roo.bootstrap.NavSidebar = function(config){
5694     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5695 };
5696
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5698     
5699     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5700     
5701     getAutoCreate : function(){
5702         
5703         
5704         return  {
5705             tag: 'div',
5706             cls: 'sidebar sidebar-nav'
5707         };
5708     
5709         
5710     }
5711     
5712     
5713     
5714 });
5715
5716
5717
5718  
5719
5720  /*
5721  * - LGPL
5722  *
5723  * nav group
5724  * 
5725  */
5726
5727 /**
5728  * @class Roo.bootstrap.NavGroup
5729  * @extends Roo.bootstrap.Component
5730  * Bootstrap NavGroup class
5731  * @cfg {String} align (left|right)
5732  * @cfg {Boolean} inverse
5733  * @cfg {String} type (nav|pills|tab) default nav
5734  * @cfg {String} navId - reference Id for navbar.
5735
5736  * 
5737  * @constructor
5738  * Create a new nav group
5739  * @param {Object} config The config object
5740  */
5741
5742 Roo.bootstrap.NavGroup = function(config){
5743     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5744     this.navItems = [];
5745    
5746     Roo.bootstrap.NavGroup.register(this);
5747      this.addEvents({
5748         /**
5749              * @event changed
5750              * Fires when the active item changes
5751              * @param {Roo.bootstrap.NavGroup} this
5752              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5754          */
5755         'changed': true
5756      });
5757     
5758 };
5759
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5761     
5762     align: '',
5763     inverse: false,
5764     form: false,
5765     type: 'nav',
5766     navId : '',
5767     // private
5768     
5769     navItems : false, 
5770     
5771     getAutoCreate : function()
5772     {
5773         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774         
5775         cfg = {
5776             tag : 'ul',
5777             cls: 'nav' 
5778         };
5779         if (Roo.bootstrap.version == 4) {
5780             if (['tabs','pills'].indexOf(this.type) != -1) {
5781                 cfg.cls += ' nav-' + this.type; 
5782             } else {
5783                 // trying to remove so header bar can right align top?
5784                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785                     // do not use on header bar... 
5786                     cfg.cls += ' navbar-nav';
5787                 }
5788             }
5789             
5790         } else {
5791             if (['tabs','pills'].indexOf(this.type) != -1) {
5792                 cfg.cls += ' nav-' + this.type
5793             } else {
5794                 if (this.type !== 'nav') {
5795                     Roo.log('nav type must be nav/tabs/pills')
5796                 }
5797                 cfg.cls += ' navbar-nav'
5798             }
5799         }
5800         
5801         if (this.parent() && this.parent().sidebar) {
5802             cfg = {
5803                 tag: 'ul',
5804                 cls: 'dashboard-menu sidebar-menu'
5805             };
5806             
5807             return cfg;
5808         }
5809         
5810         if (this.form === true) {
5811             cfg = {
5812                 tag: 'form',
5813                 cls: 'navbar-form form-inline'
5814             };
5815             //nav navbar-right ml-md-auto
5816             if (this.align === 'right') {
5817                 cfg.cls += ' navbar-right ml-md-auto';
5818             } else {
5819                 cfg.cls += ' navbar-left';
5820             }
5821         }
5822         
5823         if (this.align === 'right') {
5824             cfg.cls += ' navbar-right ml-md-auto';
5825         } else {
5826             cfg.cls += ' mr-auto';
5827         }
5828         
5829         if (this.inverse) {
5830             cfg.cls += ' navbar-inverse';
5831             
5832         }
5833         
5834         
5835         return cfg;
5836     },
5837     /**
5838     * sets the active Navigation item
5839     * @param {Roo.bootstrap.NavItem} the new current navitem
5840     */
5841     setActiveItem : function(item)
5842     {
5843         var prev = false;
5844         Roo.each(this.navItems, function(v){
5845             if (v == item) {
5846                 return ;
5847             }
5848             if (v.isActive()) {
5849                 v.setActive(false, true);
5850                 prev = v;
5851                 
5852             }
5853             
5854         });
5855
5856         item.setActive(true, true);
5857         this.fireEvent('changed', this, item, prev);
5858         
5859         
5860     },
5861     /**
5862     * gets the active Navigation item
5863     * @return {Roo.bootstrap.NavItem} the current navitem
5864     */
5865     getActive : function()
5866     {
5867         
5868         var prev = false;
5869         Roo.each(this.navItems, function(v){
5870             
5871             if (v.isActive()) {
5872                 prev = v;
5873                 
5874             }
5875             
5876         });
5877         return prev;
5878     },
5879     
5880     indexOfNav : function()
5881     {
5882         
5883         var prev = false;
5884         Roo.each(this.navItems, function(v,i){
5885             
5886             if (v.isActive()) {
5887                 prev = i;
5888                 
5889             }
5890             
5891         });
5892         return prev;
5893     },
5894     /**
5895     * adds a Navigation item
5896     * @param {Roo.bootstrap.NavItem} the navitem to add
5897     */
5898     addItem : function(cfg)
5899     {
5900         if (this.form && Roo.bootstrap.version == 4) {
5901             cfg.tag = 'div';
5902         }
5903         var cn = new Roo.bootstrap.NavItem(cfg);
5904         this.register(cn);
5905         cn.parentId = this.id;
5906         cn.onRender(this.el, null);
5907         return cn;
5908     },
5909     /**
5910     * register a Navigation item
5911     * @param {Roo.bootstrap.NavItem} the navitem to add
5912     */
5913     register : function(item)
5914     {
5915         this.navItems.push( item);
5916         item.navId = this.navId;
5917     
5918     },
5919     
5920     /**
5921     * clear all the Navigation item
5922     */
5923    
5924     clearAll : function()
5925     {
5926         this.navItems = [];
5927         this.el.dom.innerHTML = '';
5928     },
5929     
5930     getNavItem: function(tabId)
5931     {
5932         var ret = false;
5933         Roo.each(this.navItems, function(e) {
5934             if (e.tabId == tabId) {
5935                ret =  e;
5936                return false;
5937             }
5938             return true;
5939             
5940         });
5941         return ret;
5942     },
5943     
5944     setActiveNext : function()
5945     {
5946         var i = this.indexOfNav(this.getActive());
5947         if (i > this.navItems.length) {
5948             return;
5949         }
5950         this.setActiveItem(this.navItems[i+1]);
5951     },
5952     setActivePrev : function()
5953     {
5954         var i = this.indexOfNav(this.getActive());
5955         if (i  < 1) {
5956             return;
5957         }
5958         this.setActiveItem(this.navItems[i-1]);
5959     },
5960     clearWasActive : function(except) {
5961         Roo.each(this.navItems, function(e) {
5962             if (e.tabId != except.tabId && e.was_active) {
5963                e.was_active = false;
5964                return false;
5965             }
5966             return true;
5967             
5968         });
5969     },
5970     getWasActive : function ()
5971     {
5972         var r = false;
5973         Roo.each(this.navItems, function(e) {
5974             if (e.was_active) {
5975                r = e;
5976                return false;
5977             }
5978             return true;
5979             
5980         });
5981         return r;
5982     }
5983     
5984     
5985 });
5986
5987  
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5989     
5990     groups: {},
5991      /**
5992     * register a Navigation Group
5993     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5994     */
5995     register : function(navgrp)
5996     {
5997         this.groups[navgrp.navId] = navgrp;
5998         
5999     },
6000     /**
6001     * fetch a Navigation Group based on the navigation ID
6002     * @param {string} the navgroup to add
6003     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6004     */
6005     get: function(navId) {
6006         if (typeof(this.groups[navId]) == 'undefined') {
6007             return false;
6008             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6009         }
6010         return this.groups[navId] ;
6011     }
6012     
6013     
6014     
6015 });
6016
6017  /*
6018  * - LGPL
6019  *
6020  * row
6021  * 
6022  */
6023
6024 /**
6025  * @class Roo.bootstrap.NavItem
6026  * @extends Roo.bootstrap.Component
6027  * Bootstrap Navbar.NavItem class
6028  * @cfg {String} href  link to
6029  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6030
6031  * @cfg {String} html content of button
6032  * @cfg {String} badge text inside badge
6033  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6034  * @cfg {String} glyphicon DEPRICATED - use fa
6035  * @cfg {String} icon DEPRICATED - use fa
6036  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6037  * @cfg {Boolean} active Is item active
6038  * @cfg {Boolean} disabled Is item disabled
6039  
6040  * @cfg {Boolean} preventDefault (true | false) default false
6041  * @cfg {String} tabId the tab that this item activates.
6042  * @cfg {String} tagtype (a|span) render as a href or span?
6043  * @cfg {Boolean} animateRef (true|false) link to element default false  
6044   
6045  * @constructor
6046  * Create a new Navbar Item
6047  * @param {Object} config The config object
6048  */
6049 Roo.bootstrap.NavItem = function(config){
6050     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6051     this.addEvents({
6052         // raw events
6053         /**
6054          * @event click
6055          * The raw click event for the entire grid.
6056          * @param {Roo.EventObject} e
6057          */
6058         "click" : true,
6059          /**
6060             * @event changed
6061             * Fires when the active item active state changes
6062             * @param {Roo.bootstrap.NavItem} this
6063             * @param {boolean} state the new state
6064              
6065          */
6066         'changed': true,
6067         /**
6068             * @event scrollto
6069             * Fires when scroll to element
6070             * @param {Roo.bootstrap.NavItem} this
6071             * @param {Object} options
6072             * @param {Roo.EventObject} e
6073              
6074          */
6075         'scrollto': true
6076     });
6077    
6078 };
6079
6080 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6081     
6082     href: false,
6083     html: '',
6084     badge: '',
6085     icon: false,
6086     fa : false,
6087     glyphicon: false,
6088     active: false,
6089     preventDefault : false,
6090     tabId : false,
6091     tagtype : 'a',
6092     tag: 'li',
6093     disabled : false,
6094     animateRef : false,
6095     was_active : false,
6096     button_weight : '',
6097     button_outline : false,
6098     
6099     navLink: false,
6100     
6101     getAutoCreate : function(){
6102          
6103         var cfg = {
6104             tag: this.tag,
6105             cls: 'nav-item'
6106         };
6107         
6108         if (this.active) {
6109             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6110         }
6111         if (this.disabled) {
6112             cfg.cls += ' disabled';
6113         }
6114         
6115         // BS4 only?
6116         if (this.button_weight.length) {
6117             cfg.tag = this.href ? 'a' : 'button';
6118             cfg.html = this.html || '';
6119             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6120             if (this.href) {
6121                 cfg.href = this.href;
6122             }
6123             if (this.fa) {
6124                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6125             }
6126             
6127             // menu .. should add dropdown-menu class - so no need for carat..
6128             
6129             if (this.badge !== '') {
6130                  
6131                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6132             }
6133             return cfg;
6134         }
6135         
6136         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6137             cfg.cn = [
6138                 {
6139                     tag: this.tagtype,
6140                     href : this.href || "#",
6141                     html: this.html || ''
6142                 }
6143             ];
6144             if (this.tagtype == 'a') {
6145                 cfg.cn[0].cls = 'nav-link';
6146             }
6147             if (this.icon) {
6148                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6149             }
6150             if (this.fa) {
6151                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6152             }
6153             if(this.glyphicon) {
6154                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6155             }
6156             
6157             if (this.menu) {
6158                 
6159                 cfg.cn[0].html += " <span class='caret'></span>";
6160              
6161             }
6162             
6163             if (this.badge !== '') {
6164                  
6165                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6166             }
6167         }
6168         
6169         
6170         
6171         return cfg;
6172     },
6173     onRender : function(ct, position)
6174     {
6175        // Roo.log("Call onRender: " + this.xtype);
6176         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6177             this.tag = 'div';
6178         }
6179         
6180         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6181         this.navLink = this.el.select('.nav-link',true).first();
6182         return ret;
6183     },
6184       
6185     
6186     initEvents: function() 
6187     {
6188         if (typeof (this.menu) != 'undefined') {
6189             this.menu.parentType = this.xtype;
6190             this.menu.triggerEl = this.el;
6191             this.menu = this.addxtype(Roo.apply({}, this.menu));
6192         }
6193         
6194         this.el.select('a',true).on('click', this.onClick, this);
6195         
6196         if(this.tagtype == 'span'){
6197             this.el.select('span',true).on('click', this.onClick, this);
6198         }
6199        
6200         // at this point parent should be available..
6201         this.parent().register(this);
6202     },
6203     
6204     onClick : function(e)
6205     {
6206         if (e.getTarget('.dropdown-menu-item')) {
6207             // did you click on a menu itemm.... - then don't trigger onclick..
6208             return;
6209         }
6210         
6211         if(
6212                 this.preventDefault || 
6213                 this.href == '#' 
6214         ){
6215             Roo.log("NavItem - prevent Default?");
6216             e.preventDefault();
6217         }
6218         
6219         if (this.disabled) {
6220             return;
6221         }
6222         
6223         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6224         if (tg && tg.transition) {
6225             Roo.log("waiting for the transitionend");
6226             return;
6227         }
6228         
6229         
6230         
6231         //Roo.log("fire event clicked");
6232         if(this.fireEvent('click', this, e) === false){
6233             return;
6234         };
6235         
6236         if(this.tagtype == 'span'){
6237             return;
6238         }
6239         
6240         //Roo.log(this.href);
6241         var ael = this.el.select('a',true).first();
6242         //Roo.log(ael);
6243         
6244         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6245             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6246             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6247                 return; // ignore... - it's a 'hash' to another page.
6248             }
6249             Roo.log("NavItem - prevent Default?");
6250             e.preventDefault();
6251             this.scrollToElement(e);
6252         }
6253         
6254         
6255         var p =  this.parent();
6256    
6257         if (['tabs','pills'].indexOf(p.type)!==-1) {
6258             if (typeof(p.setActiveItem) !== 'undefined') {
6259                 p.setActiveItem(this);
6260             }
6261         }
6262         
6263         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6264         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6265             // remove the collapsed menu expand...
6266             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6267         }
6268     },
6269     
6270     isActive: function () {
6271         return this.active
6272     },
6273     setActive : function(state, fire, is_was_active)
6274     {
6275         if (this.active && !state && this.navId) {
6276             this.was_active = true;
6277             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6278             if (nv) {
6279                 nv.clearWasActive(this);
6280             }
6281             
6282         }
6283         this.active = state;
6284         
6285         if (!state ) {
6286             this.el.removeClass('active');
6287             this.navLink ? this.navLink.removeClass('active') : false;
6288         } else if (!this.el.hasClass('active')) {
6289             
6290             this.el.addClass('active');
6291             if (Roo.bootstrap.version == 4 && this.navLink ) {
6292                 this.navLink.addClass('active');
6293             }
6294             
6295         }
6296         if (fire) {
6297             this.fireEvent('changed', this, state);
6298         }
6299         
6300         // show a panel if it's registered and related..
6301         
6302         if (!this.navId || !this.tabId || !state || is_was_active) {
6303             return;
6304         }
6305         
6306         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6307         if (!tg) {
6308             return;
6309         }
6310         var pan = tg.getPanelByName(this.tabId);
6311         if (!pan) {
6312             return;
6313         }
6314         // if we can not flip to new panel - go back to old nav highlight..
6315         if (false == tg.showPanel(pan)) {
6316             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6317             if (nv) {
6318                 var onav = nv.getWasActive();
6319                 if (onav) {
6320                     onav.setActive(true, false, true);
6321                 }
6322             }
6323             
6324         }
6325         
6326         
6327         
6328     },
6329      // this should not be here...
6330     setDisabled : function(state)
6331     {
6332         this.disabled = state;
6333         if (!state ) {
6334             this.el.removeClass('disabled');
6335         } else if (!this.el.hasClass('disabled')) {
6336             this.el.addClass('disabled');
6337         }
6338         
6339     },
6340     
6341     /**
6342      * Fetch the element to display the tooltip on.
6343      * @return {Roo.Element} defaults to this.el
6344      */
6345     tooltipEl : function()
6346     {
6347         return this.el.select('' + this.tagtype + '', true).first();
6348     },
6349     
6350     scrollToElement : function(e)
6351     {
6352         var c = document.body;
6353         
6354         /*
6355          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6356          */
6357         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6358             c = document.documentElement;
6359         }
6360         
6361         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6362         
6363         if(!target){
6364             return;
6365         }
6366
6367         var o = target.calcOffsetsTo(c);
6368         
6369         var options = {
6370             target : target,
6371             value : o[1]
6372         };
6373         
6374         this.fireEvent('scrollto', this, options, e);
6375         
6376         Roo.get(c).scrollTo('top', options.value, true);
6377         
6378         return;
6379     }
6380 });
6381  
6382
6383  /*
6384  * - LGPL
6385  *
6386  * sidebar item
6387  *
6388  *  li
6389  *    <span> icon </span>
6390  *    <span> text </span>
6391  *    <span>badge </span>
6392  */
6393
6394 /**
6395  * @class Roo.bootstrap.NavSidebarItem
6396  * @extends Roo.bootstrap.NavItem
6397  * Bootstrap Navbar.NavSidebarItem class
6398  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6399  * {Boolean} open is the menu open
6400  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6401  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6402  * {String} buttonSize (sm|md|lg)the extra classes for the button
6403  * {Boolean} showArrow show arrow next to the text (default true)
6404  * @constructor
6405  * Create a new Navbar Button
6406  * @param {Object} config The config object
6407  */
6408 Roo.bootstrap.NavSidebarItem = function(config){
6409     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6410     this.addEvents({
6411         // raw events
6412         /**
6413          * @event click
6414          * The raw click event for the entire grid.
6415          * @param {Roo.EventObject} e
6416          */
6417         "click" : true,
6418          /**
6419             * @event changed
6420             * Fires when the active item active state changes
6421             * @param {Roo.bootstrap.NavSidebarItem} this
6422             * @param {boolean} state the new state
6423              
6424          */
6425         'changed': true
6426     });
6427    
6428 };
6429
6430 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6431     
6432     badgeWeight : 'default',
6433     
6434     open: false,
6435     
6436     buttonView : false,
6437     
6438     buttonWeight : 'default',
6439     
6440     buttonSize : 'md',
6441     
6442     showArrow : true,
6443     
6444     getAutoCreate : function(){
6445         
6446         
6447         var a = {
6448                 tag: 'a',
6449                 href : this.href || '#',
6450                 cls: '',
6451                 html : '',
6452                 cn : []
6453         };
6454         
6455         if(this.buttonView){
6456             a = {
6457                 tag: 'button',
6458                 href : this.href || '#',
6459                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6460                 html : this.html,
6461                 cn : []
6462             };
6463         }
6464         
6465         var cfg = {
6466             tag: 'li',
6467             cls: '',
6468             cn: [ a ]
6469         };
6470         
6471         if (this.active) {
6472             cfg.cls += ' active';
6473         }
6474         
6475         if (this.disabled) {
6476             cfg.cls += ' disabled';
6477         }
6478         if (this.open) {
6479             cfg.cls += ' open x-open';
6480         }
6481         // left icon..
6482         if (this.glyphicon || this.icon) {
6483             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6484             a.cn.push({ tag : 'i', cls : c }) ;
6485         }
6486         
6487         if(!this.buttonView){
6488             var span = {
6489                 tag: 'span',
6490                 html : this.html || ''
6491             };
6492
6493             a.cn.push(span);
6494             
6495         }
6496         
6497         if (this.badge !== '') {
6498             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6499         }
6500         
6501         if (this.menu) {
6502             
6503             if(this.showArrow){
6504                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6505             }
6506             
6507             a.cls += ' dropdown-toggle treeview' ;
6508         }
6509         
6510         return cfg;
6511     },
6512     
6513     initEvents : function()
6514     { 
6515         if (typeof (this.menu) != 'undefined') {
6516             this.menu.parentType = this.xtype;
6517             this.menu.triggerEl = this.el;
6518             this.menu = this.addxtype(Roo.apply({}, this.menu));
6519         }
6520         
6521         this.el.on('click', this.onClick, this);
6522         
6523         if(this.badge !== ''){
6524             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6525         }
6526         
6527     },
6528     
6529     onClick : function(e)
6530     {
6531         if(this.disabled){
6532             e.preventDefault();
6533             return;
6534         }
6535         
6536         if(this.preventDefault){
6537             e.preventDefault();
6538         }
6539         
6540         this.fireEvent('click', this, e);
6541     },
6542     
6543     disable : function()
6544     {
6545         this.setDisabled(true);
6546     },
6547     
6548     enable : function()
6549     {
6550         this.setDisabled(false);
6551     },
6552     
6553     setDisabled : function(state)
6554     {
6555         if(this.disabled == state){
6556             return;
6557         }
6558         
6559         this.disabled = state;
6560         
6561         if (state) {
6562             this.el.addClass('disabled');
6563             return;
6564         }
6565         
6566         this.el.removeClass('disabled');
6567         
6568         return;
6569     },
6570     
6571     setActive : function(state)
6572     {
6573         if(this.active == state){
6574             return;
6575         }
6576         
6577         this.active = state;
6578         
6579         if (state) {
6580             this.el.addClass('active');
6581             return;
6582         }
6583         
6584         this.el.removeClass('active');
6585         
6586         return;
6587     },
6588     
6589     isActive: function () 
6590     {
6591         return this.active;
6592     },
6593     
6594     setBadge : function(str)
6595     {
6596         if(!this.badgeEl){
6597             return;
6598         }
6599         
6600         this.badgeEl.dom.innerHTML = str;
6601     }
6602     
6603    
6604      
6605  
6606 });
6607  
6608
6609  /*
6610  * - LGPL
6611  *
6612  *  Breadcrumb Nav
6613  * 
6614  */
6615 Roo.namespace('Roo.bootstrap.breadcrumb');
6616
6617
6618 /**
6619  * @class Roo.bootstrap.breadcrumb.Nav
6620  * @extends Roo.bootstrap.Component
6621  * Bootstrap Breadcrumb Nav Class
6622  *  
6623  * @children Roo.bootstrap.breadcrumb.Item
6624  * 
6625  * @constructor
6626  * Create a new breadcrumb.Nav
6627  * @param {Object} config The config object
6628  */
6629
6630
6631 Roo.bootstrap.breadcrumb.Nav = function(config){
6632     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6633     
6634     
6635 };
6636
6637 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6638     
6639     getAutoCreate : function()
6640     {
6641
6642         var cfg = {
6643             tag: 'nav',
6644             cn : [
6645                 {
6646                     tag : 'ol',
6647                     cls : 'breadcrumb'
6648                 }
6649             ]
6650             
6651         };
6652           
6653         return cfg;
6654     },
6655     
6656     initEvents: function()
6657     {
6658         this.olEl = this.el.select('ol',true).first();    
6659     },
6660     getChildContainer : function()
6661     {
6662         return this.olEl;  
6663     }
6664     
6665 });
6666
6667  /*
6668  * - LGPL
6669  *
6670  *  Breadcrumb Item
6671  * 
6672  */
6673
6674
6675 /**
6676  * @class Roo.bootstrap.breadcrumb.Nav
6677  * @extends Roo.bootstrap.Component
6678  * Bootstrap Breadcrumb Nav Class
6679  *  
6680  * @children Roo.bootstrap.breadcrumb.Component
6681  * @cfg {String} html the content of the link.
6682  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6683  * @cfg {Boolean} active is it active
6684
6685  * 
6686  * @constructor
6687  * Create a new breadcrumb.Nav
6688  * @param {Object} config The config object
6689  */
6690
6691 Roo.bootstrap.breadcrumb.Item = function(config){
6692     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6693     this.addEvents({
6694         // img events
6695         /**
6696          * @event click
6697          * The img click event for the img.
6698          * @param {Roo.EventObject} e
6699          */
6700         "click" : true
6701     });
6702     
6703 };
6704
6705 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6706     
6707     href: false,
6708     html : '',
6709     
6710     getAutoCreate : function()
6711     {
6712
6713         var cfg = {
6714             tag: 'li',
6715             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6716         };
6717         if (this.href !== false) {
6718             cfg.cn = [{
6719                 tag : 'a',
6720                 href : this.href,
6721                 html : this.html
6722             }];
6723         } else {
6724             cfg.html = this.html;
6725         }
6726         
6727         return cfg;
6728     },
6729     
6730     initEvents: function()
6731     {
6732         if (this.href) {
6733             this.el.select('a', true).first().on('click',this.onClick, this)
6734         }
6735         
6736     },
6737     onClick : function(e)
6738     {
6739         e.preventDefault();
6740         this.fireEvent('click',this,  e);
6741     }
6742     
6743 });
6744
6745  /*
6746  * - LGPL
6747  *
6748  * row
6749  * 
6750  */
6751
6752 /**
6753  * @class Roo.bootstrap.Row
6754  * @extends Roo.bootstrap.Component
6755  * Bootstrap Row class (contains columns...)
6756  * 
6757  * @constructor
6758  * Create a new Row
6759  * @param {Object} config The config object
6760  */
6761
6762 Roo.bootstrap.Row = function(config){
6763     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6764 };
6765
6766 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6767     
6768     getAutoCreate : function(){
6769        return {
6770             cls: 'row clearfix'
6771        };
6772     }
6773     
6774     
6775 });
6776
6777  
6778
6779  /*
6780  * - LGPL
6781  *
6782  * pagination
6783  * 
6784  */
6785
6786 /**
6787  * @class Roo.bootstrap.Pagination
6788  * @extends Roo.bootstrap.Component
6789  * Bootstrap Pagination class
6790  * @cfg {String} size xs | sm | md | lg
6791  * @cfg {Boolean} inverse false | true
6792  * 
6793  * @constructor
6794  * Create a new Pagination
6795  * @param {Object} config The config object
6796  */
6797
6798 Roo.bootstrap.Pagination = function(config){
6799     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6800 };
6801
6802 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6803     
6804     cls: false,
6805     size: false,
6806     inverse: false,
6807     
6808     getAutoCreate : function(){
6809         var cfg = {
6810             tag: 'ul',
6811                 cls: 'pagination'
6812         };
6813         if (this.inverse) {
6814             cfg.cls += ' inverse';
6815         }
6816         if (this.html) {
6817             cfg.html=this.html;
6818         }
6819         if (this.cls) {
6820             cfg.cls += " " + this.cls;
6821         }
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  
6828
6829  /*
6830  * - LGPL
6831  *
6832  * Pagination item
6833  * 
6834  */
6835
6836
6837 /**
6838  * @class Roo.bootstrap.PaginationItem
6839  * @extends Roo.bootstrap.Component
6840  * Bootstrap PaginationItem class
6841  * @cfg {String} html text
6842  * @cfg {String} href the link
6843  * @cfg {Boolean} preventDefault (true | false) default true
6844  * @cfg {Boolean} active (true | false) default false
6845  * @cfg {Boolean} disabled default false
6846  * 
6847  * 
6848  * @constructor
6849  * Create a new PaginationItem
6850  * @param {Object} config The config object
6851  */
6852
6853
6854 Roo.bootstrap.PaginationItem = function(config){
6855     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6856     this.addEvents({
6857         // raw events
6858         /**
6859          * @event click
6860          * The raw click event for the entire grid.
6861          * @param {Roo.EventObject} e
6862          */
6863         "click" : true
6864     });
6865 };
6866
6867 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6868     
6869     href : false,
6870     html : false,
6871     preventDefault: true,
6872     active : false,
6873     cls : false,
6874     disabled: false,
6875     
6876     getAutoCreate : function(){
6877         var cfg= {
6878             tag: 'li',
6879             cn: [
6880                 {
6881                     tag : 'a',
6882                     href : this.href ? this.href : '#',
6883                     html : this.html ? this.html : ''
6884                 }
6885             ]
6886         };
6887         
6888         if(this.cls){
6889             cfg.cls = this.cls;
6890         }
6891         
6892         if(this.disabled){
6893             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6894         }
6895         
6896         if(this.active){
6897             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6898         }
6899         
6900         return cfg;
6901     },
6902     
6903     initEvents: function() {
6904         
6905         this.el.on('click', this.onClick, this);
6906         
6907     },
6908     onClick : function(e)
6909     {
6910         Roo.log('PaginationItem on click ');
6911         if(this.preventDefault){
6912             e.preventDefault();
6913         }
6914         
6915         if(this.disabled){
6916             return;
6917         }
6918         
6919         this.fireEvent('click', this, e);
6920     }
6921    
6922 });
6923
6924  
6925
6926  /*
6927  * - LGPL
6928  *
6929  * slider
6930  * 
6931  */
6932
6933
6934 /**
6935  * @class Roo.bootstrap.Slider
6936  * @extends Roo.bootstrap.Component
6937  * Bootstrap Slider class
6938  *    
6939  * @constructor
6940  * Create a new Slider
6941  * @param {Object} config The config object
6942  */
6943
6944 Roo.bootstrap.Slider = function(config){
6945     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6946 };
6947
6948 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6949     
6950     getAutoCreate : function(){
6951         
6952         var cfg = {
6953             tag: 'div',
6954             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6955             cn: [
6956                 {
6957                     tag: 'a',
6958                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6959                 }
6960             ]
6961         };
6962         
6963         return cfg;
6964     }
6965    
6966 });
6967
6968  /*
6969  * Based on:
6970  * Ext JS Library 1.1.1
6971  * Copyright(c) 2006-2007, Ext JS, LLC.
6972  *
6973  * Originally Released Under LGPL - original licence link has changed is not relivant.
6974  *
6975  * Fork - LGPL
6976  * <script type="text/javascript">
6977  */
6978  
6979
6980 /**
6981  * @class Roo.grid.ColumnModel
6982  * @extends Roo.util.Observable
6983  * This is the default implementation of a ColumnModel used by the Grid. It defines
6984  * the columns in the grid.
6985  * <br>Usage:<br>
6986  <pre><code>
6987  var colModel = new Roo.grid.ColumnModel([
6988         {header: "Ticker", width: 60, sortable: true, locked: true},
6989         {header: "Company Name", width: 150, sortable: true},
6990         {header: "Market Cap.", width: 100, sortable: true},
6991         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6992         {header: "Employees", width: 100, sortable: true, resizable: false}
6993  ]);
6994  </code></pre>
6995  * <p>
6996  
6997  * The config options listed for this class are options which may appear in each
6998  * individual column definition.
6999  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7000  * @constructor
7001  * @param {Object} config An Array of column config objects. See this class's
7002  * config objects for details.
7003 */
7004 Roo.grid.ColumnModel = function(config){
7005         /**
7006      * The config passed into the constructor
7007      */
7008     this.config = config;
7009     this.lookup = {};
7010
7011     // if no id, create one
7012     // if the column does not have a dataIndex mapping,
7013     // map it to the order it is in the config
7014     for(var i = 0, len = config.length; i < len; i++){
7015         var c = config[i];
7016         if(typeof c.dataIndex == "undefined"){
7017             c.dataIndex = i;
7018         }
7019         if(typeof c.renderer == "string"){
7020             c.renderer = Roo.util.Format[c.renderer];
7021         }
7022         if(typeof c.id == "undefined"){
7023             c.id = Roo.id();
7024         }
7025         if(c.editor && c.editor.xtype){
7026             c.editor  = Roo.factory(c.editor, Roo.grid);
7027         }
7028         if(c.editor && c.editor.isFormField){
7029             c.editor = new Roo.grid.GridEditor(c.editor);
7030         }
7031         this.lookup[c.id] = c;
7032     }
7033
7034     /**
7035      * The width of columns which have no width specified (defaults to 100)
7036      * @type Number
7037      */
7038     this.defaultWidth = 100;
7039
7040     /**
7041      * Default sortable of columns which have no sortable specified (defaults to false)
7042      * @type Boolean
7043      */
7044     this.defaultSortable = false;
7045
7046     this.addEvents({
7047         /**
7048              * @event widthchange
7049              * Fires when the width of a column changes.
7050              * @param {ColumnModel} this
7051              * @param {Number} columnIndex The column index
7052              * @param {Number} newWidth The new width
7053              */
7054             "widthchange": true,
7055         /**
7056              * @event headerchange
7057              * Fires when the text of a header changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newText The new header text
7061              */
7062             "headerchange": true,
7063         /**
7064              * @event hiddenchange
7065              * Fires when a column is hidden or "unhidden".
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Boolean} hidden true if hidden, false otherwise
7069              */
7070             "hiddenchange": true,
7071             /**
7072          * @event columnmoved
7073          * Fires when a column is moved.
7074          * @param {ColumnModel} this
7075          * @param {Number} oldIndex
7076          * @param {Number} newIndex
7077          */
7078         "columnmoved" : true,
7079         /**
7080          * @event columlockchange
7081          * Fires when a column's locked state is changed
7082          * @param {ColumnModel} this
7083          * @param {Number} colIndex
7084          * @param {Boolean} locked true if locked
7085          */
7086         "columnlockchange" : true
7087     });
7088     Roo.grid.ColumnModel.superclass.constructor.call(this);
7089 };
7090 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7091     /**
7092      * @cfg {String} header The header text to display in the Grid view.
7093      */
7094     /**
7095      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7096      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7097      * specified, the column's index is used as an index into the Record's data Array.
7098      */
7099     /**
7100      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7101      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7102      */
7103     /**
7104      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7105      * Defaults to the value of the {@link #defaultSortable} property.
7106      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7107      */
7108     /**
7109      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7110      */
7111     /**
7112      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7113      */
7114     /**
7115      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7116      */
7117     /**
7118      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7119      */
7120     /**
7121      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7122      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7123      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7124      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7125      */
7126        /**
7127      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7128      */
7129     /**
7130      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7131      */
7132     /**
7133      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7134      */
7135     /**
7136      * @cfg {String} cursor (Optional)
7137      */
7138     /**
7139      * @cfg {String} tooltip (Optional)
7140      */
7141     /**
7142      * @cfg {Number} xs (Optional)
7143      */
7144     /**
7145      * @cfg {Number} sm (Optional)
7146      */
7147     /**
7148      * @cfg {Number} md (Optional)
7149      */
7150     /**
7151      * @cfg {Number} lg (Optional)
7152      */
7153     /**
7154      * Returns the id of the column at the specified index.
7155      * @param {Number} index The column index
7156      * @return {String} the id
7157      */
7158     getColumnId : function(index){
7159         return this.config[index].id;
7160     },
7161
7162     /**
7163      * Returns the column for a specified id.
7164      * @param {String} id The column id
7165      * @return {Object} the column
7166      */
7167     getColumnById : function(id){
7168         return this.lookup[id];
7169     },
7170
7171     
7172     /**
7173      * Returns the column for a specified dataIndex.
7174      * @param {String} dataIndex The column dataIndex
7175      * @return {Object|Boolean} the column or false if not found
7176      */
7177     getColumnByDataIndex: function(dataIndex){
7178         var index = this.findColumnIndex(dataIndex);
7179         return index > -1 ? this.config[index] : false;
7180     },
7181     
7182     /**
7183      * Returns the index for a specified column id.
7184      * @param {String} id The column id
7185      * @return {Number} the index, or -1 if not found
7186      */
7187     getIndexById : function(id){
7188         for(var i = 0, len = this.config.length; i < len; i++){
7189             if(this.config[i].id == id){
7190                 return i;
7191             }
7192         }
7193         return -1;
7194     },
7195     
7196     /**
7197      * Returns the index for a specified column dataIndex.
7198      * @param {String} dataIndex The column dataIndex
7199      * @return {Number} the index, or -1 if not found
7200      */
7201     
7202     findColumnIndex : function(dataIndex){
7203         for(var i = 0, len = this.config.length; i < len; i++){
7204             if(this.config[i].dataIndex == dataIndex){
7205                 return i;
7206             }
7207         }
7208         return -1;
7209     },
7210     
7211     
7212     moveColumn : function(oldIndex, newIndex){
7213         var c = this.config[oldIndex];
7214         this.config.splice(oldIndex, 1);
7215         this.config.splice(newIndex, 0, c);
7216         this.dataMap = null;
7217         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7218     },
7219
7220     isLocked : function(colIndex){
7221         return this.config[colIndex].locked === true;
7222     },
7223
7224     setLocked : function(colIndex, value, suppressEvent){
7225         if(this.isLocked(colIndex) == value){
7226             return;
7227         }
7228         this.config[colIndex].locked = value;
7229         if(!suppressEvent){
7230             this.fireEvent("columnlockchange", this, colIndex, value);
7231         }
7232     },
7233
7234     getTotalLockedWidth : function(){
7235         var totalWidth = 0;
7236         for(var i = 0; i < this.config.length; i++){
7237             if(this.isLocked(i) && !this.isHidden(i)){
7238                 this.totalWidth += this.getColumnWidth(i);
7239             }
7240         }
7241         return totalWidth;
7242     },
7243
7244     getLockedCount : function(){
7245         for(var i = 0, len = this.config.length; i < len; i++){
7246             if(!this.isLocked(i)){
7247                 return i;
7248             }
7249         }
7250         
7251         return this.config.length;
7252     },
7253
7254     /**
7255      * Returns the number of columns.
7256      * @return {Number}
7257      */
7258     getColumnCount : function(visibleOnly){
7259         if(visibleOnly === true){
7260             var c = 0;
7261             for(var i = 0, len = this.config.length; i < len; i++){
7262                 if(!this.isHidden(i)){
7263                     c++;
7264                 }
7265             }
7266             return c;
7267         }
7268         return this.config.length;
7269     },
7270
7271     /**
7272      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7273      * @param {Function} fn
7274      * @param {Object} scope (optional)
7275      * @return {Array} result
7276      */
7277     getColumnsBy : function(fn, scope){
7278         var r = [];
7279         for(var i = 0, len = this.config.length; i < len; i++){
7280             var c = this.config[i];
7281             if(fn.call(scope||this, c, i) === true){
7282                 r[r.length] = c;
7283             }
7284         }
7285         return r;
7286     },
7287
7288     /**
7289      * Returns true if the specified column is sortable.
7290      * @param {Number} col The column index
7291      * @return {Boolean}
7292      */
7293     isSortable : function(col){
7294         if(typeof this.config[col].sortable == "undefined"){
7295             return this.defaultSortable;
7296         }
7297         return this.config[col].sortable;
7298     },
7299
7300     /**
7301      * Returns the rendering (formatting) function defined for the column.
7302      * @param {Number} col The column index.
7303      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7304      */
7305     getRenderer : function(col){
7306         if(!this.config[col].renderer){
7307             return Roo.grid.ColumnModel.defaultRenderer;
7308         }
7309         return this.config[col].renderer;
7310     },
7311
7312     /**
7313      * Sets the rendering (formatting) function for a column.
7314      * @param {Number} col The column index
7315      * @param {Function} fn The function to use to process the cell's raw data
7316      * to return HTML markup for the grid view. The render function is called with
7317      * the following parameters:<ul>
7318      * <li>Data value.</li>
7319      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7320      * <li>css A CSS style string to apply to the table cell.</li>
7321      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7322      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7323      * <li>Row index</li>
7324      * <li>Column index</li>
7325      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7326      */
7327     setRenderer : function(col, fn){
7328         this.config[col].renderer = fn;
7329     },
7330
7331     /**
7332      * Returns the width for the specified column.
7333      * @param {Number} col The column index
7334      * @return {Number}
7335      */
7336     getColumnWidth : function(col){
7337         return this.config[col].width * 1 || this.defaultWidth;
7338     },
7339
7340     /**
7341      * Sets the width for a column.
7342      * @param {Number} col The column index
7343      * @param {Number} width The new width
7344      */
7345     setColumnWidth : function(col, width, suppressEvent){
7346         this.config[col].width = width;
7347         this.totalWidth = null;
7348         if(!suppressEvent){
7349              this.fireEvent("widthchange", this, col, width);
7350         }
7351     },
7352
7353     /**
7354      * Returns the total width of all columns.
7355      * @param {Boolean} includeHidden True to include hidden column widths
7356      * @return {Number}
7357      */
7358     getTotalWidth : function(includeHidden){
7359         if(!this.totalWidth){
7360             this.totalWidth = 0;
7361             for(var i = 0, len = this.config.length; i < len; i++){
7362                 if(includeHidden || !this.isHidden(i)){
7363                     this.totalWidth += this.getColumnWidth(i);
7364                 }
7365             }
7366         }
7367         return this.totalWidth;
7368     },
7369
7370     /**
7371      * Returns the header for the specified column.
7372      * @param {Number} col The column index
7373      * @return {String}
7374      */
7375     getColumnHeader : function(col){
7376         return this.config[col].header;
7377     },
7378
7379     /**
7380      * Sets the header for a column.
7381      * @param {Number} col The column index
7382      * @param {String} header The new header
7383      */
7384     setColumnHeader : function(col, header){
7385         this.config[col].header = header;
7386         this.fireEvent("headerchange", this, col, header);
7387     },
7388
7389     /**
7390      * Returns the tooltip for the specified column.
7391      * @param {Number} col The column index
7392      * @return {String}
7393      */
7394     getColumnTooltip : function(col){
7395             return this.config[col].tooltip;
7396     },
7397     /**
7398      * Sets the tooltip for a column.
7399      * @param {Number} col The column index
7400      * @param {String} tooltip The new tooltip
7401      */
7402     setColumnTooltip : function(col, tooltip){
7403             this.config[col].tooltip = tooltip;
7404     },
7405
7406     /**
7407      * Returns the dataIndex for the specified column.
7408      * @param {Number} col The column index
7409      * @return {Number}
7410      */
7411     getDataIndex : function(col){
7412         return this.config[col].dataIndex;
7413     },
7414
7415     /**
7416      * Sets the dataIndex for a column.
7417      * @param {Number} col The column index
7418      * @param {Number} dataIndex The new dataIndex
7419      */
7420     setDataIndex : function(col, dataIndex){
7421         this.config[col].dataIndex = dataIndex;
7422     },
7423
7424     
7425     
7426     /**
7427      * Returns true if the cell is editable.
7428      * @param {Number} colIndex The column index
7429      * @param {Number} rowIndex The row index - this is nto actually used..?
7430      * @return {Boolean}
7431      */
7432     isCellEditable : function(colIndex, rowIndex){
7433         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7434     },
7435
7436     /**
7437      * Returns the editor defined for the cell/column.
7438      * return false or null to disable editing.
7439      * @param {Number} colIndex The column index
7440      * @param {Number} rowIndex The row index
7441      * @return {Object}
7442      */
7443     getCellEditor : function(colIndex, rowIndex){
7444         return this.config[colIndex].editor;
7445     },
7446
7447     /**
7448      * Sets if a column is editable.
7449      * @param {Number} col The column index
7450      * @param {Boolean} editable True if the column is editable
7451      */
7452     setEditable : function(col, editable){
7453         this.config[col].editable = editable;
7454     },
7455
7456
7457     /**
7458      * Returns true if the column is hidden.
7459      * @param {Number} colIndex The column index
7460      * @return {Boolean}
7461      */
7462     isHidden : function(colIndex){
7463         return this.config[colIndex].hidden;
7464     },
7465
7466
7467     /**
7468      * Returns true if the column width cannot be changed
7469      */
7470     isFixed : function(colIndex){
7471         return this.config[colIndex].fixed;
7472     },
7473
7474     /**
7475      * Returns true if the column can be resized
7476      * @return {Boolean}
7477      */
7478     isResizable : function(colIndex){
7479         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7480     },
7481     /**
7482      * Sets if a column is hidden.
7483      * @param {Number} colIndex The column index
7484      * @param {Boolean} hidden True if the column is hidden
7485      */
7486     setHidden : function(colIndex, hidden){
7487         this.config[colIndex].hidden = hidden;
7488         this.totalWidth = null;
7489         this.fireEvent("hiddenchange", this, colIndex, hidden);
7490     },
7491
7492     /**
7493      * Sets the editor for a column.
7494      * @param {Number} col The column index
7495      * @param {Object} editor The editor object
7496      */
7497     setEditor : function(col, editor){
7498         this.config[col].editor = editor;
7499     }
7500 });
7501
7502 Roo.grid.ColumnModel.defaultRenderer = function(value)
7503 {
7504     if(typeof value == "object") {
7505         return value;
7506     }
7507         if(typeof value == "string" && value.length < 1){
7508             return "&#160;";
7509         }
7510     
7511         return String.format("{0}", value);
7512 };
7513
7514 // Alias for backwards compatibility
7515 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7516 /*
7517  * Based on:
7518  * Ext JS Library 1.1.1
7519  * Copyright(c) 2006-2007, Ext JS, LLC.
7520  *
7521  * Originally Released Under LGPL - original licence link has changed is not relivant.
7522  *
7523  * Fork - LGPL
7524  * <script type="text/javascript">
7525  */
7526  
7527 /**
7528  * @class Roo.LoadMask
7529  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7530  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7531  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7532  * element's UpdateManager load indicator and will be destroyed after the initial load.
7533  * @constructor
7534  * Create a new LoadMask
7535  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7536  * @param {Object} config The config object
7537  */
7538 Roo.LoadMask = function(el, config){
7539     this.el = Roo.get(el);
7540     Roo.apply(this, config);
7541     if(this.store){
7542         this.store.on('beforeload', this.onBeforeLoad, this);
7543         this.store.on('load', this.onLoad, this);
7544         this.store.on('loadexception', this.onLoadException, this);
7545         this.removeMask = false;
7546     }else{
7547         var um = this.el.getUpdateManager();
7548         um.showLoadIndicator = false; // disable the default indicator
7549         um.on('beforeupdate', this.onBeforeLoad, this);
7550         um.on('update', this.onLoad, this);
7551         um.on('failure', this.onLoad, this);
7552         this.removeMask = true;
7553     }
7554 };
7555
7556 Roo.LoadMask.prototype = {
7557     /**
7558      * @cfg {Boolean} removeMask
7559      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7560      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7561      */
7562     /**
7563      * @cfg {String} msg
7564      * The text to display in a centered loading message box (defaults to 'Loading...')
7565      */
7566     msg : 'Loading...',
7567     /**
7568      * @cfg {String} msgCls
7569      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7570      */
7571     msgCls : 'x-mask-loading',
7572
7573     /**
7574      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7575      * @type Boolean
7576      */
7577     disabled: false,
7578
7579     /**
7580      * Disables the mask to prevent it from being displayed
7581      */
7582     disable : function(){
7583        this.disabled = true;
7584     },
7585
7586     /**
7587      * Enables the mask so that it can be displayed
7588      */
7589     enable : function(){
7590         this.disabled = false;
7591     },
7592     
7593     onLoadException : function()
7594     {
7595         Roo.log(arguments);
7596         
7597         if (typeof(arguments[3]) != 'undefined') {
7598             Roo.MessageBox.alert("Error loading",arguments[3]);
7599         } 
7600         /*
7601         try {
7602             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7603                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7604             }   
7605         } catch(e) {
7606             
7607         }
7608         */
7609     
7610         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7611     },
7612     // private
7613     onLoad : function()
7614     {
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617
7618     // private
7619     onBeforeLoad : function(){
7620         if(!this.disabled){
7621             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7622         }
7623     },
7624
7625     // private
7626     destroy : function(){
7627         if(this.store){
7628             this.store.un('beforeload', this.onBeforeLoad, this);
7629             this.store.un('load', this.onLoad, this);
7630             this.store.un('loadexception', this.onLoadException, this);
7631         }else{
7632             var um = this.el.getUpdateManager();
7633             um.un('beforeupdate', this.onBeforeLoad, this);
7634             um.un('update', this.onLoad, this);
7635             um.un('failure', this.onLoad, this);
7636         }
7637     }
7638 };/*
7639  * - LGPL
7640  *
7641  * table
7642  * 
7643  */
7644
7645 /**
7646  * @class Roo.bootstrap.Table
7647  * @extends Roo.bootstrap.Component
7648  * Bootstrap Table class
7649  * @cfg {String} cls table class
7650  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7651  * @cfg {String} bgcolor Specifies the background color for a table
7652  * @cfg {Number} border Specifies whether the table cells should have borders or not
7653  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7654  * @cfg {Number} cellspacing Specifies the space between cells
7655  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7656  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7657  * @cfg {String} sortable Specifies that the table should be sortable
7658  * @cfg {String} summary Specifies a summary of the content of a table
7659  * @cfg {Number} width Specifies the width of a table
7660  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7661  * 
7662  * @cfg {boolean} striped Should the rows be alternative striped
7663  * @cfg {boolean} bordered Add borders to the table
7664  * @cfg {boolean} hover Add hover highlighting
7665  * @cfg {boolean} condensed Format condensed
7666  * @cfg {boolean} responsive Format condensed
7667  * @cfg {Boolean} loadMask (true|false) default false
7668  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7669  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7670  * @cfg {Boolean} rowSelection (true|false) default false
7671  * @cfg {Boolean} cellSelection (true|false) default false
7672  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7673  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7674  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7675  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7676  
7677  * 
7678  * @constructor
7679  * Create a new Table
7680  * @param {Object} config The config object
7681  */
7682
7683 Roo.bootstrap.Table = function(config){
7684     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7685     
7686   
7687     
7688     // BC...
7689     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7690     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7691     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7692     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7693     
7694     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7695     if (this.sm) {
7696         this.sm.grid = this;
7697         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7698         this.sm = this.selModel;
7699         this.sm.xmodule = this.xmodule || false;
7700     }
7701     
7702     if (this.cm && typeof(this.cm.config) == 'undefined') {
7703         this.colModel = new Roo.grid.ColumnModel(this.cm);
7704         this.cm = this.colModel;
7705         this.cm.xmodule = this.xmodule || false;
7706     }
7707     if (this.store) {
7708         this.store= Roo.factory(this.store, Roo.data);
7709         this.ds = this.store;
7710         this.ds.xmodule = this.xmodule || false;
7711          
7712     }
7713     if (this.footer && this.store) {
7714         this.footer.dataSource = this.ds;
7715         this.footer = Roo.factory(this.footer);
7716     }
7717     
7718     /** @private */
7719     this.addEvents({
7720         /**
7721          * @event cellclick
7722          * Fires when a cell is clicked
7723          * @param {Roo.bootstrap.Table} this
7724          * @param {Roo.Element} el
7725          * @param {Number} rowIndex
7726          * @param {Number} columnIndex
7727          * @param {Roo.EventObject} e
7728          */
7729         "cellclick" : true,
7730         /**
7731          * @event celldblclick
7732          * Fires when a cell is double clicked
7733          * @param {Roo.bootstrap.Table} this
7734          * @param {Roo.Element} el
7735          * @param {Number} rowIndex
7736          * @param {Number} columnIndex
7737          * @param {Roo.EventObject} e
7738          */
7739         "celldblclick" : true,
7740         /**
7741          * @event rowclick
7742          * Fires when a row is clicked
7743          * @param {Roo.bootstrap.Table} this
7744          * @param {Roo.Element} el
7745          * @param {Number} rowIndex
7746          * @param {Roo.EventObject} e
7747          */
7748         "rowclick" : true,
7749         /**
7750          * @event rowdblclick
7751          * Fires when a row is double clicked
7752          * @param {Roo.bootstrap.Table} this
7753          * @param {Roo.Element} el
7754          * @param {Number} rowIndex
7755          * @param {Roo.EventObject} e
7756          */
7757         "rowdblclick" : true,
7758         /**
7759          * @event mouseover
7760          * Fires when a mouseover occur
7761          * @param {Roo.bootstrap.Table} this
7762          * @param {Roo.Element} el
7763          * @param {Number} rowIndex
7764          * @param {Number} columnIndex
7765          * @param {Roo.EventObject} e
7766          */
7767         "mouseover" : true,
7768         /**
7769          * @event mouseout
7770          * Fires when a mouseout occur
7771          * @param {Roo.bootstrap.Table} this
7772          * @param {Roo.Element} el
7773          * @param {Number} rowIndex
7774          * @param {Number} columnIndex
7775          * @param {Roo.EventObject} e
7776          */
7777         "mouseout" : true,
7778         /**
7779          * @event rowclass
7780          * Fires when a row is rendered, so you can change add a style to it.
7781          * @param {Roo.bootstrap.Table} this
7782          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7783          */
7784         'rowclass' : true,
7785           /**
7786          * @event rowsrendered
7787          * Fires when all the  rows have been rendered
7788          * @param {Roo.bootstrap.Table} this
7789          */
7790         'rowsrendered' : true,
7791         /**
7792          * @event contextmenu
7793          * The raw contextmenu event for the entire grid.
7794          * @param {Roo.EventObject} e
7795          */
7796         "contextmenu" : true,
7797         /**
7798          * @event rowcontextmenu
7799          * Fires when a row is right clicked
7800          * @param {Roo.bootstrap.Table} this
7801          * @param {Number} rowIndex
7802          * @param {Roo.EventObject} e
7803          */
7804         "rowcontextmenu" : true,
7805         /**
7806          * @event cellcontextmenu
7807          * Fires when a cell is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Number} cellIndex
7811          * @param {Roo.EventObject} e
7812          */
7813          "cellcontextmenu" : true,
7814          /**
7815          * @event headercontextmenu
7816          * Fires when a header is right clicked
7817          * @param {Roo.bootstrap.Table} this
7818          * @param {Number} columnIndex
7819          * @param {Roo.EventObject} e
7820          */
7821         "headercontextmenu" : true
7822     });
7823 };
7824
7825 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7826     
7827     cls: false,
7828     align: false,
7829     bgcolor: false,
7830     border: false,
7831     cellpadding: false,
7832     cellspacing: false,
7833     frame: false,
7834     rules: false,
7835     sortable: false,
7836     summary: false,
7837     width: false,
7838     striped : false,
7839     scrollBody : false,
7840     bordered: false,
7841     hover:  false,
7842     condensed : false,
7843     responsive : false,
7844     sm : false,
7845     cm : false,
7846     store : false,
7847     loadMask : false,
7848     footerShow : true,
7849     headerShow : true,
7850   
7851     rowSelection : false,
7852     cellSelection : false,
7853     layout : false,
7854     
7855     // Roo.Element - the tbody
7856     mainBody: false,
7857     // Roo.Element - thead element
7858     mainHead: false,
7859     
7860     container: false, // used by gridpanel...
7861     
7862     lazyLoad : false,
7863     
7864     CSS : Roo.util.CSS,
7865     
7866     auto_hide_footer : false,
7867     
7868     getAutoCreate : function()
7869     {
7870         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7871         
7872         cfg = {
7873             tag: 'table',
7874             cls : 'table',
7875             cn : []
7876         };
7877         if (this.scrollBody) {
7878             cfg.cls += ' table-body-fixed';
7879         }    
7880         if (this.striped) {
7881             cfg.cls += ' table-striped';
7882         }
7883         
7884         if (this.hover) {
7885             cfg.cls += ' table-hover';
7886         }
7887         if (this.bordered) {
7888             cfg.cls += ' table-bordered';
7889         }
7890         if (this.condensed) {
7891             cfg.cls += ' table-condensed';
7892         }
7893         if (this.responsive) {
7894             cfg.cls += ' table-responsive';
7895         }
7896         
7897         if (this.cls) {
7898             cfg.cls+=  ' ' +this.cls;
7899         }
7900         
7901         // this lot should be simplifed...
7902         var _t = this;
7903         var cp = [
7904             'align',
7905             'bgcolor',
7906             'border',
7907             'cellpadding',
7908             'cellspacing',
7909             'frame',
7910             'rules',
7911             'sortable',
7912             'summary',
7913             'width'
7914         ].forEach(function(k) {
7915             if (_t[k]) {
7916                 cfg[k] = _t[k];
7917             }
7918         });
7919         
7920         
7921         if (this.layout) {
7922             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7923         }
7924         
7925         if(this.store || this.cm){
7926             if(this.headerShow){
7927                 cfg.cn.push(this.renderHeader());
7928             }
7929             
7930             cfg.cn.push(this.renderBody());
7931             
7932             if(this.footerShow){
7933                 cfg.cn.push(this.renderFooter());
7934             }
7935             // where does this come from?
7936             //cfg.cls+=  ' TableGrid';
7937         }
7938         
7939         return { cn : [ cfg ] };
7940     },
7941     
7942     initEvents : function()
7943     {   
7944         if(!this.store || !this.cm){
7945             return;
7946         }
7947         if (this.selModel) {
7948             this.selModel.initEvents();
7949         }
7950         
7951         
7952         //Roo.log('initEvents with ds!!!!');
7953         
7954         this.mainBody = this.el.select('tbody', true).first();
7955         this.mainHead = this.el.select('thead', true).first();
7956         this.mainFoot = this.el.select('tfoot', true).first();
7957         
7958         
7959         
7960         var _this = this;
7961         
7962         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7963             e.on('click', _this.sort, _this);
7964         });
7965         
7966         this.mainBody.on("click", this.onClick, this);
7967         this.mainBody.on("dblclick", this.onDblClick, this);
7968         
7969         // why is this done????? = it breaks dialogs??
7970         //this.parent().el.setStyle('position', 'relative');
7971         
7972         
7973         if (this.footer) {
7974             this.footer.parentId = this.id;
7975             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7976             
7977             if(this.lazyLoad){
7978                 this.el.select('tfoot tr td').first().addClass('hide');
7979             }
7980         } 
7981         
7982         if(this.loadMask) {
7983             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7984         }
7985         
7986         this.store.on('load', this.onLoad, this);
7987         this.store.on('beforeload', this.onBeforeLoad, this);
7988         this.store.on('update', this.onUpdate, this);
7989         this.store.on('add', this.onAdd, this);
7990         this.store.on("clear", this.clear, this);
7991         
7992         this.el.on("contextmenu", this.onContextMenu, this);
7993         
7994         this.mainBody.on('scroll', this.onBodyScroll, this);
7995         
7996         this.cm.on("headerchange", this.onHeaderChange, this);
7997         
7998         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7999         
8000     },
8001     
8002     onContextMenu : function(e, t)
8003     {
8004         this.processEvent("contextmenu", e);
8005     },
8006     
8007     processEvent : function(name, e)
8008     {
8009         if (name != 'touchstart' ) {
8010             this.fireEvent(name, e);    
8011         }
8012         
8013         var t = e.getTarget();
8014         
8015         var cell = Roo.get(t);
8016         
8017         if(!cell){
8018             return;
8019         }
8020         
8021         if(cell.findParent('tfoot', false, true)){
8022             return;
8023         }
8024         
8025         if(cell.findParent('thead', false, true)){
8026             
8027             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8028                 cell = Roo.get(t).findParent('th', false, true);
8029                 if (!cell) {
8030                     Roo.log("failed to find th in thead?");
8031                     Roo.log(e.getTarget());
8032                     return;
8033                 }
8034             }
8035             
8036             var cellIndex = cell.dom.cellIndex;
8037             
8038             var ename = name == 'touchstart' ? 'click' : name;
8039             this.fireEvent("header" + ename, this, cellIndex, e);
8040             
8041             return;
8042         }
8043         
8044         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8045             cell = Roo.get(t).findParent('td', false, true);
8046             if (!cell) {
8047                 Roo.log("failed to find th in tbody?");
8048                 Roo.log(e.getTarget());
8049                 return;
8050             }
8051         }
8052         
8053         var row = cell.findParent('tr', false, true);
8054         var cellIndex = cell.dom.cellIndex;
8055         var rowIndex = row.dom.rowIndex - 1;
8056         
8057         if(row !== false){
8058             
8059             this.fireEvent("row" + name, this, rowIndex, e);
8060             
8061             if(cell !== false){
8062             
8063                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8064             }
8065         }
8066         
8067     },
8068     
8069     onMouseover : function(e, el)
8070     {
8071         var cell = Roo.get(el);
8072         
8073         if(!cell){
8074             return;
8075         }
8076         
8077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8078             cell = cell.findParent('td', false, true);
8079         }
8080         
8081         var row = cell.findParent('tr', false, true);
8082         var cellIndex = cell.dom.cellIndex;
8083         var rowIndex = row.dom.rowIndex - 1; // start from 0
8084         
8085         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8086         
8087     },
8088     
8089     onMouseout : function(e, el)
8090     {
8091         var cell = Roo.get(el);
8092         
8093         if(!cell){
8094             return;
8095         }
8096         
8097         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8098             cell = cell.findParent('td', false, true);
8099         }
8100         
8101         var row = cell.findParent('tr', false, true);
8102         var cellIndex = cell.dom.cellIndex;
8103         var rowIndex = row.dom.rowIndex - 1; // start from 0
8104         
8105         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8106         
8107     },
8108     
8109     onClick : function(e, el)
8110     {
8111         var cell = Roo.get(el);
8112         
8113         if(!cell || (!this.cellSelection && !this.rowSelection)){
8114             return;
8115         }
8116         
8117         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8118             cell = cell.findParent('td', false, true);
8119         }
8120         
8121         if(!cell || typeof(cell) == 'undefined'){
8122             return;
8123         }
8124         
8125         var row = cell.findParent('tr', false, true);
8126         
8127         if(!row || typeof(row) == 'undefined'){
8128             return;
8129         }
8130         
8131         var cellIndex = cell.dom.cellIndex;
8132         var rowIndex = this.getRowIndex(row);
8133         
8134         // why??? - should these not be based on SelectionModel?
8135         if(this.cellSelection){
8136             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8137         }
8138         
8139         if(this.rowSelection){
8140             this.fireEvent('rowclick', this, row, rowIndex, e);
8141         }
8142         
8143         
8144     },
8145         
8146     onDblClick : function(e,el)
8147     {
8148         var cell = Roo.get(el);
8149         
8150         if(!cell || (!this.cellSelection && !this.rowSelection)){
8151             return;
8152         }
8153         
8154         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8155             cell = cell.findParent('td', false, true);
8156         }
8157         
8158         if(!cell || typeof(cell) == 'undefined'){
8159             return;
8160         }
8161         
8162         var row = cell.findParent('tr', false, true);
8163         
8164         if(!row || typeof(row) == 'undefined'){
8165             return;
8166         }
8167         
8168         var cellIndex = cell.dom.cellIndex;
8169         var rowIndex = this.getRowIndex(row);
8170         
8171         if(this.cellSelection){
8172             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8173         }
8174         
8175         if(this.rowSelection){
8176             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8177         }
8178     },
8179     
8180     sort : function(e,el)
8181     {
8182         var col = Roo.get(el);
8183         
8184         if(!col.hasClass('sortable')){
8185             return;
8186         }
8187         
8188         var sort = col.attr('sort');
8189         var dir = 'ASC';
8190         
8191         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8192             dir = 'DESC';
8193         }
8194         
8195         this.store.sortInfo = {field : sort, direction : dir};
8196         
8197         if (this.footer) {
8198             Roo.log("calling footer first");
8199             this.footer.onClick('first');
8200         } else {
8201         
8202             this.store.load({ params : { start : 0 } });
8203         }
8204     },
8205     
8206     renderHeader : function()
8207     {
8208         var header = {
8209             tag: 'thead',
8210             cn : []
8211         };
8212         
8213         var cm = this.cm;
8214         this.totalWidth = 0;
8215         
8216         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8217             
8218             var config = cm.config[i];
8219             
8220             var c = {
8221                 tag: 'th',
8222                 cls : 'x-hcol-' + i,
8223                 style : '',
8224                 html: cm.getColumnHeader(i)
8225             };
8226             
8227             var hh = '';
8228             
8229             if(typeof(config.sortable) != 'undefined' && config.sortable){
8230                 c.cls = 'sortable';
8231                 c.html = '<i class="glyphicon"></i>' + c.html;
8232             }
8233             
8234             // could use BS4 hidden-..-down 
8235             
8236             if(typeof(config.lgHeader) != 'undefined'){
8237                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8238             }
8239             
8240             if(typeof(config.mdHeader) != 'undefined'){
8241                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8242             }
8243             
8244             if(typeof(config.smHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.xsHeader) != 'undefined'){
8249                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8250             }
8251             
8252             if(hh.length){
8253                 c.html = hh;
8254             }
8255             
8256             if(typeof(config.tooltip) != 'undefined'){
8257                 c.tooltip = config.tooltip;
8258             }
8259             
8260             if(typeof(config.colspan) != 'undefined'){
8261                 c.colspan = config.colspan;
8262             }
8263             
8264             if(typeof(config.hidden) != 'undefined' && config.hidden){
8265                 c.style += ' display:none;';
8266             }
8267             
8268             if(typeof(config.dataIndex) != 'undefined'){
8269                 c.sort = config.dataIndex;
8270             }
8271             
8272            
8273             
8274             if(typeof(config.align) != 'undefined' && config.align.length){
8275                 c.style += ' text-align:' + config.align + ';';
8276             }
8277             
8278             if(typeof(config.width) != 'undefined'){
8279                 c.style += ' width:' + config.width + 'px;';
8280                 this.totalWidth += config.width;
8281             } else {
8282                 this.totalWidth += 100; // assume minimum of 100 per column?
8283             }
8284             
8285             if(typeof(config.cls) != 'undefined'){
8286                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8287             }
8288             
8289             ['xs','sm','md','lg'].map(function(size){
8290                 
8291                 if(typeof(config[size]) == 'undefined'){
8292                     return;
8293                 }
8294                  
8295                 if (!config[size]) { // 0 = hidden
8296                     // BS 4 '0' is treated as hide that column and below.
8297                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8298                     return;
8299                 }
8300                 
8301                 c.cls += ' col-' + size + '-' + config[size] + (
8302                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8303                 );
8304                 
8305                 
8306             });
8307             
8308             header.cn.push(c)
8309         }
8310         
8311         return header;
8312     },
8313     
8314     renderBody : function()
8315     {
8316         var body = {
8317             tag: 'tbody',
8318             cn : [
8319                 {
8320                     tag: 'tr',
8321                     cn : [
8322                         {
8323                             tag : 'td',
8324                             colspan :  this.cm.getColumnCount()
8325                         }
8326                     ]
8327                 }
8328             ]
8329         };
8330         
8331         return body;
8332     },
8333     
8334     renderFooter : function()
8335     {
8336         var footer = {
8337             tag: 'tfoot',
8338             cn : [
8339                 {
8340                     tag: 'tr',
8341                     cn : [
8342                         {
8343                             tag : 'td',
8344                             colspan :  this.cm.getColumnCount()
8345                         }
8346                     ]
8347                 }
8348             ]
8349         };
8350         
8351         return footer;
8352     },
8353     
8354     
8355     
8356     onLoad : function()
8357     {
8358 //        Roo.log('ds onload');
8359         this.clear();
8360         
8361         var _this = this;
8362         var cm = this.cm;
8363         var ds = this.store;
8364         
8365         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8366             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8367             if (_this.store.sortInfo) {
8368                     
8369                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8370                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8371                 }
8372                 
8373                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8374                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8375                 }
8376             }
8377         });
8378         
8379         var tbody =  this.mainBody;
8380               
8381         if(ds.getCount() > 0){
8382             ds.data.each(function(d,rowIndex){
8383                 var row =  this.renderRow(cm, ds, rowIndex);
8384                 
8385                 tbody.createChild(row);
8386                 
8387                 var _this = this;
8388                 
8389                 if(row.cellObjects.length){
8390                     Roo.each(row.cellObjects, function(r){
8391                         _this.renderCellObject(r);
8392                     })
8393                 }
8394                 
8395             }, this);
8396         }
8397         
8398         var tfoot = this.el.select('tfoot', true).first();
8399         
8400         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8401             
8402             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8403             
8404             var total = this.ds.getTotalCount();
8405             
8406             if(this.footer.pageSize < total){
8407                 this.mainFoot.show();
8408             }
8409         }
8410         
8411         Roo.each(this.el.select('tbody td', true).elements, function(e){
8412             e.on('mouseover', _this.onMouseover, _this);
8413         });
8414         
8415         Roo.each(this.el.select('tbody td', true).elements, function(e){
8416             e.on('mouseout', _this.onMouseout, _this);
8417         });
8418         this.fireEvent('rowsrendered', this);
8419         
8420         this.autoSize();
8421     },
8422     
8423     
8424     onUpdate : function(ds,record)
8425     {
8426         this.refreshRow(record);
8427         this.autoSize();
8428     },
8429     
8430     onRemove : function(ds, record, index, isUpdate){
8431         if(isUpdate !== true){
8432             this.fireEvent("beforerowremoved", this, index, record);
8433         }
8434         var bt = this.mainBody.dom;
8435         
8436         var rows = this.el.select('tbody > tr', true).elements;
8437         
8438         if(typeof(rows[index]) != 'undefined'){
8439             bt.removeChild(rows[index].dom);
8440         }
8441         
8442 //        if(bt.rows[index]){
8443 //            bt.removeChild(bt.rows[index]);
8444 //        }
8445         
8446         if(isUpdate !== true){
8447             //this.stripeRows(index);
8448             //this.syncRowHeights(index, index);
8449             //this.layout();
8450             this.fireEvent("rowremoved", this, index, record);
8451         }
8452     },
8453     
8454     onAdd : function(ds, records, rowIndex)
8455     {
8456         //Roo.log('on Add called');
8457         // - note this does not handle multiple adding very well..
8458         var bt = this.mainBody.dom;
8459         for (var i =0 ; i < records.length;i++) {
8460             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8461             //Roo.log(records[i]);
8462             //Roo.log(this.store.getAt(rowIndex+i));
8463             this.insertRow(this.store, rowIndex + i, false);
8464             return;
8465         }
8466         
8467     },
8468     
8469     
8470     refreshRow : function(record){
8471         var ds = this.store, index;
8472         if(typeof record == 'number'){
8473             index = record;
8474             record = ds.getAt(index);
8475         }else{
8476             index = ds.indexOf(record);
8477             if (index < 0) {
8478                 return; // should not happen - but seems to 
8479             }
8480         }
8481         this.insertRow(ds, index, true);
8482         this.autoSize();
8483         this.onRemove(ds, record, index+1, true);
8484         this.autoSize();
8485         //this.syncRowHeights(index, index);
8486         //this.layout();
8487         this.fireEvent("rowupdated", this, index, record);
8488     },
8489     
8490     insertRow : function(dm, rowIndex, isUpdate){
8491         
8492         if(!isUpdate){
8493             this.fireEvent("beforerowsinserted", this, rowIndex);
8494         }
8495             //var s = this.getScrollState();
8496         var row = this.renderRow(this.cm, this.store, rowIndex);
8497         // insert before rowIndex..
8498         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8499         
8500         var _this = this;
8501                 
8502         if(row.cellObjects.length){
8503             Roo.each(row.cellObjects, function(r){
8504                 _this.renderCellObject(r);
8505             })
8506         }
8507             
8508         if(!isUpdate){
8509             this.fireEvent("rowsinserted", this, rowIndex);
8510             //this.syncRowHeights(firstRow, lastRow);
8511             //this.stripeRows(firstRow);
8512             //this.layout();
8513         }
8514         
8515     },
8516     
8517     
8518     getRowDom : function(rowIndex)
8519     {
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8523         
8524     },
8525     // returns the object tree for a tr..
8526   
8527     
8528     renderRow : function(cm, ds, rowIndex) 
8529     {
8530         var d = ds.getAt(rowIndex);
8531         
8532         var row = {
8533             tag : 'tr',
8534             cls : 'x-row-' + rowIndex,
8535             cn : []
8536         };
8537             
8538         var cellObjects = [];
8539         
8540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8541             var config = cm.config[i];
8542             
8543             var renderer = cm.getRenderer(i);
8544             var value = '';
8545             var id = false;
8546             
8547             if(typeof(renderer) !== 'undefined'){
8548                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8549             }
8550             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8551             // and are rendered into the cells after the row is rendered - using the id for the element.
8552             
8553             if(typeof(value) === 'object'){
8554                 id = Roo.id();
8555                 cellObjects.push({
8556                     container : id,
8557                     cfg : value 
8558                 })
8559             }
8560             
8561             var rowcfg = {
8562                 record: d,
8563                 rowIndex : rowIndex,
8564                 colIndex : i,
8565                 rowClass : ''
8566             };
8567
8568             this.fireEvent('rowclass', this, rowcfg);
8569             
8570             var td = {
8571                 tag: 'td',
8572                 cls : rowcfg.rowClass + ' x-col-' + i,
8573                 style: '',
8574                 html: (typeof(value) === 'object') ? '' : value
8575             };
8576             
8577             if (id) {
8578                 td.id = id;
8579             }
8580             
8581             if(typeof(config.colspan) != 'undefined'){
8582                 td.colspan = config.colspan;
8583             }
8584             
8585             if(typeof(config.hidden) != 'undefined' && config.hidden){
8586                 td.style += ' display:none;';
8587             }
8588             
8589             if(typeof(config.align) != 'undefined' && config.align.length){
8590                 td.style += ' text-align:' + config.align + ';';
8591             }
8592             if(typeof(config.valign) != 'undefined' && config.valign.length){
8593                 td.style += ' vertical-align:' + config.valign + ';';
8594             }
8595             
8596             if(typeof(config.width) != 'undefined'){
8597                 td.style += ' width:' +  config.width + 'px;';
8598             }
8599             
8600             if(typeof(config.cursor) != 'undefined'){
8601                 td.style += ' cursor:' +  config.cursor + ';';
8602             }
8603             
8604             if(typeof(config.cls) != 'undefined'){
8605                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8606             }
8607             
8608             ['xs','sm','md','lg'].map(function(size){
8609                 
8610                 if(typeof(config[size]) == 'undefined'){
8611                     return;
8612                 }
8613                 
8614                 
8615                   
8616                 if (!config[size]) { // 0 = hidden
8617                     // BS 4 '0' is treated as hide that column and below.
8618                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8619                     return;
8620                 }
8621                 
8622                 td.cls += ' col-' + size + '-' + config[size] + (
8623                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8624                 );
8625                  
8626
8627             });
8628             
8629             row.cn.push(td);
8630            
8631         }
8632         
8633         row.cellObjects = cellObjects;
8634         
8635         return row;
8636           
8637     },
8638     
8639     
8640     
8641     onBeforeLoad : function()
8642     {
8643         
8644     },
8645      /**
8646      * Remove all rows
8647      */
8648     clear : function()
8649     {
8650         this.el.select('tbody', true).first().dom.innerHTML = '';
8651     },
8652     /**
8653      * Show or hide a row.
8654      * @param {Number} rowIndex to show or hide
8655      * @param {Boolean} state hide
8656      */
8657     setRowVisibility : function(rowIndex, state)
8658     {
8659         var bt = this.mainBody.dom;
8660         
8661         var rows = this.el.select('tbody > tr', true).elements;
8662         
8663         if(typeof(rows[rowIndex]) == 'undefined'){
8664             return;
8665         }
8666         rows[rowIndex].dom.style.display = state ? '' : 'none';
8667     },
8668     
8669     
8670     getSelectionModel : function(){
8671         if(!this.selModel){
8672             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8673         }
8674         return this.selModel;
8675     },
8676     /*
8677      * Render the Roo.bootstrap object from renderder
8678      */
8679     renderCellObject : function(r)
8680     {
8681         var _this = this;
8682         
8683         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8684         
8685         var t = r.cfg.render(r.container);
8686         
8687         if(r.cfg.cn){
8688             Roo.each(r.cfg.cn, function(c){
8689                 var child = {
8690                     container: t.getChildContainer(),
8691                     cfg: c
8692                 };
8693                 _this.renderCellObject(child);
8694             })
8695         }
8696     },
8697     
8698     getRowIndex : function(row)
8699     {
8700         var rowIndex = -1;
8701         
8702         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8703             if(el != row){
8704                 return;
8705             }
8706             
8707             rowIndex = index;
8708         });
8709         
8710         return rowIndex;
8711     },
8712      /**
8713      * Returns the grid's underlying element = used by panel.Grid
8714      * @return {Element} The element
8715      */
8716     getGridEl : function(){
8717         return this.el;
8718     },
8719      /**
8720      * Forces a resize - used by panel.Grid
8721      * @return {Element} The element
8722      */
8723     autoSize : function()
8724     {
8725         //var ctr = Roo.get(this.container.dom.parentElement);
8726         var ctr = Roo.get(this.el.dom);
8727         
8728         var thd = this.getGridEl().select('thead',true).first();
8729         var tbd = this.getGridEl().select('tbody', true).first();
8730         var tfd = this.getGridEl().select('tfoot', true).first();
8731         
8732         var cw = ctr.getWidth();
8733         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8734         
8735         if (tbd) {
8736             
8737             tbd.setWidth(ctr.getWidth());
8738             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8739             // this needs fixing for various usage - currently only hydra job advers I think..
8740             //tdb.setHeight(
8741             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8742             //); 
8743             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8744             cw -= barsize;
8745         }
8746         cw = Math.max(cw, this.totalWidth);
8747         this.getGridEl().select('tbody tr',true).setWidth(cw);
8748         
8749         // resize 'expandable coloumn?
8750         
8751         return; // we doe not have a view in this design..
8752         
8753     },
8754     onBodyScroll: function()
8755     {
8756         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8757         if(this.mainHead){
8758             this.mainHead.setStyle({
8759                 'position' : 'relative',
8760                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8761             });
8762         }
8763         
8764         if(this.lazyLoad){
8765             
8766             var scrollHeight = this.mainBody.dom.scrollHeight;
8767             
8768             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8769             
8770             var height = this.mainBody.getHeight();
8771             
8772             if(scrollHeight - height == scrollTop) {
8773                 
8774                 var total = this.ds.getTotalCount();
8775                 
8776                 if(this.footer.cursor + this.footer.pageSize < total){
8777                     
8778                     this.footer.ds.load({
8779                         params : {
8780                             start : this.footer.cursor + this.footer.pageSize,
8781                             limit : this.footer.pageSize
8782                         },
8783                         add : true
8784                     });
8785                 }
8786             }
8787             
8788         }
8789     },
8790     
8791     onHeaderChange : function()
8792     {
8793         var header = this.renderHeader();
8794         var table = this.el.select('table', true).first();
8795         
8796         this.mainHead.remove();
8797         this.mainHead = table.createChild(header, this.mainBody, false);
8798     },
8799     
8800     onHiddenChange : function(colModel, colIndex, hidden)
8801     {
8802         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8803         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8804         
8805         this.CSS.updateRule(thSelector, "display", "");
8806         this.CSS.updateRule(tdSelector, "display", "");
8807         
8808         if(hidden){
8809             this.CSS.updateRule(thSelector, "display", "none");
8810             this.CSS.updateRule(tdSelector, "display", "none");
8811         }
8812         
8813         this.onHeaderChange();
8814         this.onLoad();
8815     },
8816     
8817     setColumnWidth: function(col_index, width)
8818     {
8819         // width = "md-2 xs-2..."
8820         if(!this.colModel.config[col_index]) {
8821             return;
8822         }
8823         
8824         var w = width.split(" ");
8825         
8826         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8827         
8828         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8829         
8830         
8831         for(var j = 0; j < w.length; j++) {
8832             
8833             if(!w[j]) {
8834                 continue;
8835             }
8836             
8837             var size_cls = w[j].split("-");
8838             
8839             if(!Number.isInteger(size_cls[1] * 1)) {
8840                 continue;
8841             }
8842             
8843             if(!this.colModel.config[col_index][size_cls[0]]) {
8844                 continue;
8845             }
8846             
8847             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8848                 continue;
8849             }
8850             
8851             h_row[0].classList.replace(
8852                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8853                 "col-"+size_cls[0]+"-"+size_cls[1]
8854             );
8855             
8856             for(var i = 0; i < rows.length; i++) {
8857                 
8858                 var size_cls = w[j].split("-");
8859                 
8860                 if(!Number.isInteger(size_cls[1] * 1)) {
8861                     continue;
8862                 }
8863                 
8864                 if(!this.colModel.config[col_index][size_cls[0]]) {
8865                     continue;
8866                 }
8867                 
8868                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8869                     continue;
8870                 }
8871                 
8872                 rows[i].classList.replace(
8873                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8874                     "col-"+size_cls[0]+"-"+size_cls[1]
8875                 );
8876             }
8877             
8878             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8879         }
8880     }
8881 });
8882
8883  
8884
8885  /*
8886  * - LGPL
8887  *
8888  * table cell
8889  * 
8890  */
8891
8892 /**
8893  * @class Roo.bootstrap.TableCell
8894  * @extends Roo.bootstrap.Component
8895  * Bootstrap TableCell class
8896  * @cfg {String} html cell contain text
8897  * @cfg {String} cls cell class
8898  * @cfg {String} tag cell tag (td|th) default td
8899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8900  * @cfg {String} align Aligns the content in a cell
8901  * @cfg {String} axis Categorizes cells
8902  * @cfg {String} bgcolor Specifies the background color of a cell
8903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8904  * @cfg {Number} colspan Specifies the number of columns a cell should span
8905  * @cfg {String} headers Specifies one or more header cells a cell is related to
8906  * @cfg {Number} height Sets the height of a cell
8907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8908  * @cfg {Number} rowspan Sets the number of rows a cell should span
8909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8910  * @cfg {String} valign Vertical aligns the content in a cell
8911  * @cfg {Number} width Specifies the width of a cell
8912  * 
8913  * @constructor
8914  * Create a new TableCell
8915  * @param {Object} config The config object
8916  */
8917
8918 Roo.bootstrap.TableCell = function(config){
8919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8920 };
8921
8922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8923     
8924     html: false,
8925     cls: false,
8926     tag: false,
8927     abbr: false,
8928     align: false,
8929     axis: false,
8930     bgcolor: false,
8931     charoff: false,
8932     colspan: false,
8933     headers: false,
8934     height: false,
8935     nowrap: false,
8936     rowspan: false,
8937     scope: false,
8938     valign: false,
8939     width: false,
8940     
8941     
8942     getAutoCreate : function(){
8943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8944         
8945         cfg = {
8946             tag: 'td'
8947         };
8948         
8949         if(this.tag){
8950             cfg.tag = this.tag;
8951         }
8952         
8953         if (this.html) {
8954             cfg.html=this.html
8955         }
8956         if (this.cls) {
8957             cfg.cls=this.cls
8958         }
8959         if (this.abbr) {
8960             cfg.abbr=this.abbr
8961         }
8962         if (this.align) {
8963             cfg.align=this.align
8964         }
8965         if (this.axis) {
8966             cfg.axis=this.axis
8967         }
8968         if (this.bgcolor) {
8969             cfg.bgcolor=this.bgcolor
8970         }
8971         if (this.charoff) {
8972             cfg.charoff=this.charoff
8973         }
8974         if (this.colspan) {
8975             cfg.colspan=this.colspan
8976         }
8977         if (this.headers) {
8978             cfg.headers=this.headers
8979         }
8980         if (this.height) {
8981             cfg.height=this.height
8982         }
8983         if (this.nowrap) {
8984             cfg.nowrap=this.nowrap
8985         }
8986         if (this.rowspan) {
8987             cfg.rowspan=this.rowspan
8988         }
8989         if (this.scope) {
8990             cfg.scope=this.scope
8991         }
8992         if (this.valign) {
8993             cfg.valign=this.valign
8994         }
8995         if (this.width) {
8996             cfg.width=this.width
8997         }
8998         
8999         
9000         return cfg;
9001     }
9002    
9003 });
9004
9005  
9006
9007  /*
9008  * - LGPL
9009  *
9010  * table row
9011  * 
9012  */
9013
9014 /**
9015  * @class Roo.bootstrap.TableRow
9016  * @extends Roo.bootstrap.Component
9017  * Bootstrap TableRow class
9018  * @cfg {String} cls row class
9019  * @cfg {String} align Aligns the content in a table row
9020  * @cfg {String} bgcolor Specifies a background color for a table row
9021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9022  * @cfg {String} valign Vertical aligns the content in a table row
9023  * 
9024  * @constructor
9025  * Create a new TableRow
9026  * @param {Object} config The config object
9027  */
9028
9029 Roo.bootstrap.TableRow = function(config){
9030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9031 };
9032
9033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9034     
9035     cls: false,
9036     align: false,
9037     bgcolor: false,
9038     charoff: false,
9039     valign: false,
9040     
9041     getAutoCreate : function(){
9042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9043         
9044         cfg = {
9045             tag: 'tr'
9046         };
9047             
9048         if(this.cls){
9049             cfg.cls = this.cls;
9050         }
9051         if(this.align){
9052             cfg.align = this.align;
9053         }
9054         if(this.bgcolor){
9055             cfg.bgcolor = this.bgcolor;
9056         }
9057         if(this.charoff){
9058             cfg.charoff = this.charoff;
9059         }
9060         if(this.valign){
9061             cfg.valign = this.valign;
9062         }
9063         
9064         return cfg;
9065     }
9066    
9067 });
9068
9069  
9070
9071  /*
9072  * - LGPL
9073  *
9074  * table body
9075  * 
9076  */
9077
9078 /**
9079  * @class Roo.bootstrap.TableBody
9080  * @extends Roo.bootstrap.Component
9081  * Bootstrap TableBody class
9082  * @cfg {String} cls element class
9083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9084  * @cfg {String} align Aligns the content inside the element
9085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9087  * 
9088  * @constructor
9089  * Create a new TableBody
9090  * @param {Object} config The config object
9091  */
9092
9093 Roo.bootstrap.TableBody = function(config){
9094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9095 };
9096
9097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9098     
9099     cls: false,
9100     tag: false,
9101     align: false,
9102     charoff: false,
9103     valign: false,
9104     
9105     getAutoCreate : function(){
9106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9107         
9108         cfg = {
9109             tag: 'tbody'
9110         };
9111             
9112         if (this.cls) {
9113             cfg.cls=this.cls
9114         }
9115         if(this.tag){
9116             cfg.tag = this.tag;
9117         }
9118         
9119         if(this.align){
9120             cfg.align = this.align;
9121         }
9122         if(this.charoff){
9123             cfg.charoff = this.charoff;
9124         }
9125         if(this.valign){
9126             cfg.valign = this.valign;
9127         }
9128         
9129         return cfg;
9130     }
9131     
9132     
9133 //    initEvents : function()
9134 //    {
9135 //        
9136 //        if(!this.store){
9137 //            return;
9138 //        }
9139 //        
9140 //        this.store = Roo.factory(this.store, Roo.data);
9141 //        this.store.on('load', this.onLoad, this);
9142 //        
9143 //        this.store.load();
9144 //        
9145 //    },
9146 //    
9147 //    onLoad: function () 
9148 //    {   
9149 //        this.fireEvent('load', this);
9150 //    }
9151 //    
9152 //   
9153 });
9154
9155  
9156
9157  /*
9158  * Based on:
9159  * Ext JS Library 1.1.1
9160  * Copyright(c) 2006-2007, Ext JS, LLC.
9161  *
9162  * Originally Released Under LGPL - original licence link has changed is not relivant.
9163  *
9164  * Fork - LGPL
9165  * <script type="text/javascript">
9166  */
9167
9168 // as we use this in bootstrap.
9169 Roo.namespace('Roo.form');
9170  /**
9171  * @class Roo.form.Action
9172  * Internal Class used to handle form actions
9173  * @constructor
9174  * @param {Roo.form.BasicForm} el The form element or its id
9175  * @param {Object} config Configuration options
9176  */
9177
9178  
9179  
9180 // define the action interface
9181 Roo.form.Action = function(form, options){
9182     this.form = form;
9183     this.options = options || {};
9184 };
9185 /**
9186  * Client Validation Failed
9187  * @const 
9188  */
9189 Roo.form.Action.CLIENT_INVALID = 'client';
9190 /**
9191  * Server Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.SERVER_INVALID = 'server';
9195  /**
9196  * Connect to Server Failed
9197  * @const 
9198  */
9199 Roo.form.Action.CONNECT_FAILURE = 'connect';
9200 /**
9201  * Reading Data from Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.LOAD_FAILURE = 'load';
9205
9206 Roo.form.Action.prototype = {
9207     type : 'default',
9208     failureType : undefined,
9209     response : undefined,
9210     result : undefined,
9211
9212     // interface method
9213     run : function(options){
9214
9215     },
9216
9217     // interface method
9218     success : function(response){
9219
9220     },
9221
9222     // interface method
9223     handleResponse : function(response){
9224
9225     },
9226
9227     // default connection failure
9228     failure : function(response){
9229         
9230         this.response = response;
9231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9232         this.form.afterAction(this, false);
9233     },
9234
9235     processResponse : function(response){
9236         this.response = response;
9237         if(!response.responseText){
9238             return true;
9239         }
9240         this.result = this.handleResponse(response);
9241         return this.result;
9242     },
9243
9244     // utility functions used internally
9245     getUrl : function(appendParams){
9246         var url = this.options.url || this.form.url || this.form.el.dom.action;
9247         if(appendParams){
9248             var p = this.getParams();
9249             if(p){
9250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9251             }
9252         }
9253         return url;
9254     },
9255
9256     getMethod : function(){
9257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9258     },
9259
9260     getParams : function(){
9261         var bp = this.form.baseParams;
9262         var p = this.options.params;
9263         if(p){
9264             if(typeof p == "object"){
9265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9266             }else if(typeof p == 'string' && bp){
9267                 p += '&' + Roo.urlEncode(bp);
9268             }
9269         }else if(bp){
9270             p = Roo.urlEncode(bp);
9271         }
9272         return p;
9273     },
9274
9275     createCallback : function(){
9276         return {
9277             success: this.success,
9278             failure: this.failure,
9279             scope: this,
9280             timeout: (this.form.timeout*1000),
9281             upload: this.form.fileUpload ? this.success : undefined
9282         };
9283     }
9284 };
9285
9286 Roo.form.Action.Submit = function(form, options){
9287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9288 };
9289
9290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9291     type : 'submit',
9292
9293     haveProgress : false,
9294     uploadComplete : false,
9295     
9296     // uploadProgress indicator.
9297     uploadProgress : function()
9298     {
9299         if (!this.form.progressUrl) {
9300             return;
9301         }
9302         
9303         if (!this.haveProgress) {
9304             Roo.MessageBox.progress("Uploading", "Uploading");
9305         }
9306         if (this.uploadComplete) {
9307            Roo.MessageBox.hide();
9308            return;
9309         }
9310         
9311         this.haveProgress = true;
9312    
9313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9314         
9315         var c = new Roo.data.Connection();
9316         c.request({
9317             url : this.form.progressUrl,
9318             params: {
9319                 id : uid
9320             },
9321             method: 'GET',
9322             success : function(req){
9323                //console.log(data);
9324                 var rdata = false;
9325                 var edata;
9326                 try  {
9327                    rdata = Roo.decode(req.responseText)
9328                 } catch (e) {
9329                     Roo.log("Invalid data from server..");
9330                     Roo.log(edata);
9331                     return;
9332                 }
9333                 if (!rdata || !rdata.success) {
9334                     Roo.log(rdata);
9335                     Roo.MessageBox.alert(Roo.encode(rdata));
9336                     return;
9337                 }
9338                 var data = rdata.data;
9339                 
9340                 if (this.uploadComplete) {
9341                    Roo.MessageBox.hide();
9342                    return;
9343                 }
9344                    
9345                 if (data){
9346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9348                     );
9349                 }
9350                 this.uploadProgress.defer(2000,this);
9351             },
9352        
9353             failure: function(data) {
9354                 Roo.log('progress url failed ');
9355                 Roo.log(data);
9356             },
9357             scope : this
9358         });
9359            
9360     },
9361     
9362     
9363     run : function()
9364     {
9365         // run get Values on the form, so it syncs any secondary forms.
9366         this.form.getValues();
9367         
9368         var o = this.options;
9369         var method = this.getMethod();
9370         var isPost = method == 'POST';
9371         if(o.clientValidation === false || this.form.isValid()){
9372             
9373             if (this.form.progressUrl) {
9374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9375                     (new Date() * 1) + '' + Math.random());
9376                     
9377             } 
9378             
9379             
9380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9381                 form:this.form.el.dom,
9382                 url:this.getUrl(!isPost),
9383                 method: method,
9384                 params:isPost ? this.getParams() : null,
9385                 isUpload: this.form.fileUpload,
9386                 formData : this.form.formData
9387             }));
9388             
9389             this.uploadProgress();
9390
9391         }else if (o.clientValidation !== false){ // client validation failed
9392             this.failureType = Roo.form.Action.CLIENT_INVALID;
9393             this.form.afterAction(this, false);
9394         }
9395     },
9396
9397     success : function(response)
9398     {
9399         this.uploadComplete= true;
9400         if (this.haveProgress) {
9401             Roo.MessageBox.hide();
9402         }
9403         
9404         
9405         var result = this.processResponse(response);
9406         if(result === true || result.success){
9407             this.form.afterAction(this, true);
9408             return;
9409         }
9410         if(result.errors){
9411             this.form.markInvalid(result.errors);
9412             this.failureType = Roo.form.Action.SERVER_INVALID;
9413         }
9414         this.form.afterAction(this, false);
9415     },
9416     failure : function(response)
9417     {
9418         this.uploadComplete= true;
9419         if (this.haveProgress) {
9420             Roo.MessageBox.hide();
9421         }
9422         
9423         this.response = response;
9424         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9425         this.form.afterAction(this, false);
9426     },
9427     
9428     handleResponse : function(response){
9429         if(this.form.errorReader){
9430             var rs = this.form.errorReader.read(response);
9431             var errors = [];
9432             if(rs.records){
9433                 for(var i = 0, len = rs.records.length; i < len; i++) {
9434                     var r = rs.records[i];
9435                     errors[i] = r.data;
9436                 }
9437             }
9438             if(errors.length < 1){
9439                 errors = null;
9440             }
9441             return {
9442                 success : rs.success,
9443                 errors : errors
9444             };
9445         }
9446         var ret = false;
9447         try {
9448             ret = Roo.decode(response.responseText);
9449         } catch (e) {
9450             ret = {
9451                 success: false,
9452                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9453                 errors : []
9454             };
9455         }
9456         return ret;
9457         
9458     }
9459 });
9460
9461
9462 Roo.form.Action.Load = function(form, options){
9463     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9464     this.reader = this.form.reader;
9465 };
9466
9467 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9468     type : 'load',
9469
9470     run : function(){
9471         
9472         Roo.Ajax.request(Roo.apply(
9473                 this.createCallback(), {
9474                     method:this.getMethod(),
9475                     url:this.getUrl(false),
9476                     params:this.getParams()
9477         }));
9478     },
9479
9480     success : function(response){
9481         
9482         var result = this.processResponse(response);
9483         if(result === true || !result.success || !result.data){
9484             this.failureType = Roo.form.Action.LOAD_FAILURE;
9485             this.form.afterAction(this, false);
9486             return;
9487         }
9488         this.form.clearInvalid();
9489         this.form.setValues(result.data);
9490         this.form.afterAction(this, true);
9491     },
9492
9493     handleResponse : function(response){
9494         if(this.form.reader){
9495             var rs = this.form.reader.read(response);
9496             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9497             return {
9498                 success : rs.success,
9499                 data : data
9500             };
9501         }
9502         return Roo.decode(response.responseText);
9503     }
9504 });
9505
9506 Roo.form.Action.ACTION_TYPES = {
9507     'load' : Roo.form.Action.Load,
9508     'submit' : Roo.form.Action.Submit
9509 };/*
9510  * - LGPL
9511  *
9512  * form
9513  *
9514  */
9515
9516 /**
9517  * @class Roo.bootstrap.Form
9518  * @extends Roo.bootstrap.Component
9519  * Bootstrap Form class
9520  * @cfg {String} method  GET | POST (default POST)
9521  * @cfg {String} labelAlign top | left (default top)
9522  * @cfg {String} align left  | right - for navbars
9523  * @cfg {Boolean} loadMask load mask when submit (default true)
9524
9525  *
9526  * @constructor
9527  * Create a new Form
9528  * @param {Object} config The config object
9529  */
9530
9531
9532 Roo.bootstrap.Form = function(config){
9533     
9534     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9535     
9536     Roo.bootstrap.Form.popover.apply();
9537     
9538     this.addEvents({
9539         /**
9540          * @event clientvalidation
9541          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9542          * @param {Form} this
9543          * @param {Boolean} valid true if the form has passed client-side validation
9544          */
9545         clientvalidation: true,
9546         /**
9547          * @event beforeaction
9548          * Fires before any action is performed. Return false to cancel the action.
9549          * @param {Form} this
9550          * @param {Action} action The action to be performed
9551          */
9552         beforeaction: true,
9553         /**
9554          * @event actionfailed
9555          * Fires when an action fails.
9556          * @param {Form} this
9557          * @param {Action} action The action that failed
9558          */
9559         actionfailed : true,
9560         /**
9561          * @event actioncomplete
9562          * Fires when an action is completed.
9563          * @param {Form} this
9564          * @param {Action} action The action that completed
9565          */
9566         actioncomplete : true
9567     });
9568 };
9569
9570 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9571
9572      /**
9573      * @cfg {String} method
9574      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9575      */
9576     method : 'POST',
9577     /**
9578      * @cfg {String} url
9579      * The URL to use for form actions if one isn't supplied in the action options.
9580      */
9581     /**
9582      * @cfg {Boolean} fileUpload
9583      * Set to true if this form is a file upload.
9584      */
9585
9586     /**
9587      * @cfg {Object} baseParams
9588      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9589      */
9590
9591     /**
9592      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9593      */
9594     timeout: 30,
9595     /**
9596      * @cfg {Sting} align (left|right) for navbar forms
9597      */
9598     align : 'left',
9599
9600     // private
9601     activeAction : null,
9602
9603     /**
9604      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9605      * element by passing it or its id or mask the form itself by passing in true.
9606      * @type Mixed
9607      */
9608     waitMsgTarget : false,
9609
9610     loadMask : true,
9611     
9612     /**
9613      * @cfg {Boolean} errorMask (true|false) default false
9614      */
9615     errorMask : false,
9616     
9617     /**
9618      * @cfg {Number} maskOffset Default 100
9619      */
9620     maskOffset : 100,
9621     
9622     /**
9623      * @cfg {Boolean} maskBody
9624      */
9625     maskBody : false,
9626
9627     getAutoCreate : function(){
9628
9629         var cfg = {
9630             tag: 'form',
9631             method : this.method || 'POST',
9632             id : this.id || Roo.id(),
9633             cls : ''
9634         };
9635         if (this.parent().xtype.match(/^Nav/)) {
9636             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9637
9638         }
9639
9640         if (this.labelAlign == 'left' ) {
9641             cfg.cls += ' form-horizontal';
9642         }
9643
9644
9645         return cfg;
9646     },
9647     initEvents : function()
9648     {
9649         this.el.on('submit', this.onSubmit, this);
9650         // this was added as random key presses on the form where triggering form submit.
9651         this.el.on('keypress', function(e) {
9652             if (e.getCharCode() != 13) {
9653                 return true;
9654             }
9655             // we might need to allow it for textareas.. and some other items.
9656             // check e.getTarget().
9657
9658             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9659                 return true;
9660             }
9661
9662             Roo.log("keypress blocked");
9663
9664             e.preventDefault();
9665             return false;
9666         });
9667         
9668     },
9669     // private
9670     onSubmit : function(e){
9671         e.stopEvent();
9672     },
9673
9674      /**
9675      * Returns true if client-side validation on the form is successful.
9676      * @return Boolean
9677      */
9678     isValid : function(){
9679         var items = this.getItems();
9680         var valid = true;
9681         var target = false;
9682         
9683         items.each(function(f){
9684             
9685             if(f.validate()){
9686                 return;
9687             }
9688             
9689             Roo.log('invalid field: ' + f.name);
9690             
9691             valid = false;
9692
9693             if(!target && f.el.isVisible(true)){
9694                 target = f;
9695             }
9696            
9697         });
9698         
9699         if(this.errorMask && !valid){
9700             Roo.bootstrap.Form.popover.mask(this, target);
9701         }
9702         
9703         return valid;
9704     },
9705     
9706     /**
9707      * Returns true if any fields in this form have changed since their original load.
9708      * @return Boolean
9709      */
9710     isDirty : function(){
9711         var dirty = false;
9712         var items = this.getItems();
9713         items.each(function(f){
9714            if(f.isDirty()){
9715                dirty = true;
9716                return false;
9717            }
9718            return true;
9719         });
9720         return dirty;
9721     },
9722      /**
9723      * Performs a predefined action (submit or load) or custom actions you define on this form.
9724      * @param {String} actionName The name of the action type
9725      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9726      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9727      * accept other config options):
9728      * <pre>
9729 Property          Type             Description
9730 ----------------  ---------------  ----------------------------------------------------------------------------------
9731 url               String           The url for the action (defaults to the form's url)
9732 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9733 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9734 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9735                                    validate the form on the client (defaults to false)
9736      * </pre>
9737      * @return {BasicForm} this
9738      */
9739     doAction : function(action, options){
9740         if(typeof action == 'string'){
9741             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9742         }
9743         if(this.fireEvent('beforeaction', this, action) !== false){
9744             this.beforeAction(action);
9745             action.run.defer(100, action);
9746         }
9747         return this;
9748     },
9749
9750     // private
9751     beforeAction : function(action){
9752         var o = action.options;
9753         
9754         if(this.loadMask){
9755             
9756             if(this.maskBody){
9757                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9758             } else {
9759                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9760             }
9761         }
9762         // not really supported yet.. ??
9763
9764         //if(this.waitMsgTarget === true){
9765         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9766         //}else if(this.waitMsgTarget){
9767         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9768         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9769         //}else {
9770         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9771        // }
9772
9773     },
9774
9775     // private
9776     afterAction : function(action, success){
9777         this.activeAction = null;
9778         var o = action.options;
9779
9780         if(this.loadMask){
9781             
9782             if(this.maskBody){
9783                 Roo.get(document.body).unmask();
9784             } else {
9785                 this.el.unmask();
9786             }
9787         }
9788         
9789         //if(this.waitMsgTarget === true){
9790 //            this.el.unmask();
9791         //}else if(this.waitMsgTarget){
9792         //    this.waitMsgTarget.unmask();
9793         //}else{
9794         //    Roo.MessageBox.updateProgress(1);
9795         //    Roo.MessageBox.hide();
9796        // }
9797         //
9798         if(success){
9799             if(o.reset){
9800                 this.reset();
9801             }
9802             Roo.callback(o.success, o.scope, [this, action]);
9803             this.fireEvent('actioncomplete', this, action);
9804
9805         }else{
9806
9807             // failure condition..
9808             // we have a scenario where updates need confirming.
9809             // eg. if a locking scenario exists..
9810             // we look for { errors : { needs_confirm : true }} in the response.
9811             if (
9812                 (typeof(action.result) != 'undefined')  &&
9813                 (typeof(action.result.errors) != 'undefined')  &&
9814                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9815            ){
9816                 var _t = this;
9817                 Roo.log("not supported yet");
9818                  /*
9819
9820                 Roo.MessageBox.confirm(
9821                     "Change requires confirmation",
9822                     action.result.errorMsg,
9823                     function(r) {
9824                         if (r != 'yes') {
9825                             return;
9826                         }
9827                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9828                     }
9829
9830                 );
9831                 */
9832
9833
9834                 return;
9835             }
9836
9837             Roo.callback(o.failure, o.scope, [this, action]);
9838             // show an error message if no failed handler is set..
9839             if (!this.hasListener('actionfailed')) {
9840                 Roo.log("need to add dialog support");
9841                 /*
9842                 Roo.MessageBox.alert("Error",
9843                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9844                         action.result.errorMsg :
9845                         "Saving Failed, please check your entries or try again"
9846                 );
9847                 */
9848             }
9849
9850             this.fireEvent('actionfailed', this, action);
9851         }
9852
9853     },
9854     /**
9855      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9856      * @param {String} id The value to search for
9857      * @return Field
9858      */
9859     findField : function(id){
9860         var items = this.getItems();
9861         var field = items.get(id);
9862         if(!field){
9863              items.each(function(f){
9864                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9865                     field = f;
9866                     return false;
9867                 }
9868                 return true;
9869             });
9870         }
9871         return field || null;
9872     },
9873      /**
9874      * Mark fields in this form invalid in bulk.
9875      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9876      * @return {BasicForm} this
9877      */
9878     markInvalid : function(errors){
9879         if(errors instanceof Array){
9880             for(var i = 0, len = errors.length; i < len; i++){
9881                 var fieldError = errors[i];
9882                 var f = this.findField(fieldError.id);
9883                 if(f){
9884                     f.markInvalid(fieldError.msg);
9885                 }
9886             }
9887         }else{
9888             var field, id;
9889             for(id in errors){
9890                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9891                     field.markInvalid(errors[id]);
9892                 }
9893             }
9894         }
9895         //Roo.each(this.childForms || [], function (f) {
9896         //    f.markInvalid(errors);
9897         //});
9898
9899         return this;
9900     },
9901
9902     /**
9903      * Set values for fields in this form in bulk.
9904      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9905      * @return {BasicForm} this
9906      */
9907     setValues : function(values){
9908         if(values instanceof Array){ // array of objects
9909             for(var i = 0, len = values.length; i < len; i++){
9910                 var v = values[i];
9911                 var f = this.findField(v.id);
9912                 if(f){
9913                     f.setValue(v.value);
9914                     if(this.trackResetOnLoad){
9915                         f.originalValue = f.getValue();
9916                     }
9917                 }
9918             }
9919         }else{ // object hash
9920             var field, id;
9921             for(id in values){
9922                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9923
9924                     if (field.setFromData &&
9925                         field.valueField &&
9926                         field.displayField &&
9927                         // combos' with local stores can
9928                         // be queried via setValue()
9929                         // to set their value..
9930                         (field.store && !field.store.isLocal)
9931                         ) {
9932                         // it's a combo
9933                         var sd = { };
9934                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9935                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9936                         field.setFromData(sd);
9937
9938                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9939                         
9940                         field.setFromData(values);
9941                         
9942                     } else {
9943                         field.setValue(values[id]);
9944                     }
9945
9946
9947                     if(this.trackResetOnLoad){
9948                         field.originalValue = field.getValue();
9949                     }
9950                 }
9951             }
9952         }
9953
9954         //Roo.each(this.childForms || [], function (f) {
9955         //    f.setValues(values);
9956         //});
9957
9958         return this;
9959     },
9960
9961     /**
9962      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9963      * they are returned as an array.
9964      * @param {Boolean} asString
9965      * @return {Object}
9966      */
9967     getValues : function(asString){
9968         //if (this.childForms) {
9969             // copy values from the child forms
9970         //    Roo.each(this.childForms, function (f) {
9971         //        this.setValues(f.getValues());
9972         //    }, this);
9973         //}
9974
9975
9976
9977         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9978         if(asString === true){
9979             return fs;
9980         }
9981         return Roo.urlDecode(fs);
9982     },
9983
9984     /**
9985      * Returns the fields in this form as an object with key/value pairs.
9986      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9987      * @return {Object}
9988      */
9989     getFieldValues : function(with_hidden)
9990     {
9991         var items = this.getItems();
9992         var ret = {};
9993         items.each(function(f){
9994             
9995             if (!f.getName()) {
9996                 return;
9997             }
9998             
9999             var v = f.getValue();
10000             
10001             if (f.inputType =='radio') {
10002                 if (typeof(ret[f.getName()]) == 'undefined') {
10003                     ret[f.getName()] = ''; // empty..
10004                 }
10005
10006                 if (!f.el.dom.checked) {
10007                     return;
10008
10009                 }
10010                 v = f.el.dom.value;
10011
10012             }
10013             
10014             if(f.xtype == 'MoneyField'){
10015                 ret[f.currencyName] = f.getCurrency();
10016             }
10017
10018             // not sure if this supported any more..
10019             if ((typeof(v) == 'object') && f.getRawValue) {
10020                 v = f.getRawValue() ; // dates..
10021             }
10022             // combo boxes where name != hiddenName...
10023             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10024                 ret[f.name] = f.getRawValue();
10025             }
10026             ret[f.getName()] = v;
10027         });
10028
10029         return ret;
10030     },
10031
10032     /**
10033      * Clears all invalid messages in this form.
10034      * @return {BasicForm} this
10035      */
10036     clearInvalid : function(){
10037         var items = this.getItems();
10038
10039         items.each(function(f){
10040            f.clearInvalid();
10041         });
10042
10043         return this;
10044     },
10045
10046     /**
10047      * Resets this form.
10048      * @return {BasicForm} this
10049      */
10050     reset : function(){
10051         var items = this.getItems();
10052         items.each(function(f){
10053             f.reset();
10054         });
10055
10056         Roo.each(this.childForms || [], function (f) {
10057             f.reset();
10058         });
10059
10060
10061         return this;
10062     },
10063     
10064     getItems : function()
10065     {
10066         var r=new Roo.util.MixedCollection(false, function(o){
10067             return o.id || (o.id = Roo.id());
10068         });
10069         var iter = function(el) {
10070             if (el.inputEl) {
10071                 r.add(el);
10072             }
10073             if (!el.items) {
10074                 return;
10075             }
10076             Roo.each(el.items,function(e) {
10077                 iter(e);
10078             });
10079         };
10080
10081         iter(this);
10082         return r;
10083     },
10084     
10085     hideFields : function(items)
10086     {
10087         Roo.each(items, function(i){
10088             
10089             var f = this.findField(i);
10090             
10091             if(!f){
10092                 return;
10093             }
10094             
10095             f.hide();
10096             
10097         }, this);
10098     },
10099     
10100     showFields : function(items)
10101     {
10102         Roo.each(items, function(i){
10103             
10104             var f = this.findField(i);
10105             
10106             if(!f){
10107                 return;
10108             }
10109             
10110             f.show();
10111             
10112         }, this);
10113     }
10114
10115 });
10116
10117 Roo.apply(Roo.bootstrap.Form, {
10118     
10119     popover : {
10120         
10121         padding : 5,
10122         
10123         isApplied : false,
10124         
10125         isMasked : false,
10126         
10127         form : false,
10128         
10129         target : false,
10130         
10131         toolTip : false,
10132         
10133         intervalID : false,
10134         
10135         maskEl : false,
10136         
10137         apply : function()
10138         {
10139             if(this.isApplied){
10140                 return;
10141             }
10142             
10143             this.maskEl = {
10144                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10145                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10146                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10147                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10148             };
10149             
10150             this.maskEl.top.enableDisplayMode("block");
10151             this.maskEl.left.enableDisplayMode("block");
10152             this.maskEl.bottom.enableDisplayMode("block");
10153             this.maskEl.right.enableDisplayMode("block");
10154             
10155             this.toolTip = new Roo.bootstrap.Tooltip({
10156                 cls : 'roo-form-error-popover',
10157                 alignment : {
10158                     'left' : ['r-l', [-2,0], 'right'],
10159                     'right' : ['l-r', [2,0], 'left'],
10160                     'bottom' : ['tl-bl', [0,2], 'top'],
10161                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10162                 }
10163             });
10164             
10165             this.toolTip.render(Roo.get(document.body));
10166
10167             this.toolTip.el.enableDisplayMode("block");
10168             
10169             Roo.get(document.body).on('click', function(){
10170                 this.unmask();
10171             }, this);
10172             
10173             Roo.get(document.body).on('touchstart', function(){
10174                 this.unmask();
10175             }, this);
10176             
10177             this.isApplied = true
10178         },
10179         
10180         mask : function(form, target)
10181         {
10182             this.form = form;
10183             
10184             this.target = target;
10185             
10186             if(!this.form.errorMask || !target.el){
10187                 return;
10188             }
10189             
10190             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10191             
10192             Roo.log(scrollable);
10193             
10194             var ot = this.target.el.calcOffsetsTo(scrollable);
10195             
10196             var scrollTo = ot[1] - this.form.maskOffset;
10197             
10198             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10199             
10200             scrollable.scrollTo('top', scrollTo);
10201             
10202             var box = this.target.el.getBox();
10203             Roo.log(box);
10204             var zIndex = Roo.bootstrap.Modal.zIndex++;
10205
10206             
10207             this.maskEl.top.setStyle('position', 'absolute');
10208             this.maskEl.top.setStyle('z-index', zIndex);
10209             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10210             this.maskEl.top.setLeft(0);
10211             this.maskEl.top.setTop(0);
10212             this.maskEl.top.show();
10213             
10214             this.maskEl.left.setStyle('position', 'absolute');
10215             this.maskEl.left.setStyle('z-index', zIndex);
10216             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10217             this.maskEl.left.setLeft(0);
10218             this.maskEl.left.setTop(box.y - this.padding);
10219             this.maskEl.left.show();
10220
10221             this.maskEl.bottom.setStyle('position', 'absolute');
10222             this.maskEl.bottom.setStyle('z-index', zIndex);
10223             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10224             this.maskEl.bottom.setLeft(0);
10225             this.maskEl.bottom.setTop(box.bottom + this.padding);
10226             this.maskEl.bottom.show();
10227
10228             this.maskEl.right.setStyle('position', 'absolute');
10229             this.maskEl.right.setStyle('z-index', zIndex);
10230             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10231             this.maskEl.right.setLeft(box.right + this.padding);
10232             this.maskEl.right.setTop(box.y - this.padding);
10233             this.maskEl.right.show();
10234
10235             this.toolTip.bindEl = this.target.el;
10236
10237             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10238
10239             var tip = this.target.blankText;
10240
10241             if(this.target.getValue() !== '' ) {
10242                 
10243                 if (this.target.invalidText.length) {
10244                     tip = this.target.invalidText;
10245                 } else if (this.target.regexText.length){
10246                     tip = this.target.regexText;
10247                 }
10248             }
10249
10250             this.toolTip.show(tip);
10251
10252             this.intervalID = window.setInterval(function() {
10253                 Roo.bootstrap.Form.popover.unmask();
10254             }, 10000);
10255
10256             window.onwheel = function(){ return false;};
10257             
10258             (function(){ this.isMasked = true; }).defer(500, this);
10259             
10260         },
10261         
10262         unmask : function()
10263         {
10264             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10265                 return;
10266             }
10267             
10268             this.maskEl.top.setStyle('position', 'absolute');
10269             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10270             this.maskEl.top.hide();
10271
10272             this.maskEl.left.setStyle('position', 'absolute');
10273             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10274             this.maskEl.left.hide();
10275
10276             this.maskEl.bottom.setStyle('position', 'absolute');
10277             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.bottom.hide();
10279
10280             this.maskEl.right.setStyle('position', 'absolute');
10281             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.right.hide();
10283             
10284             this.toolTip.hide();
10285             
10286             this.toolTip.el.hide();
10287             
10288             window.onwheel = function(){ return true;};
10289             
10290             if(this.intervalID){
10291                 window.clearInterval(this.intervalID);
10292                 this.intervalID = false;
10293             }
10294             
10295             this.isMasked = false;
10296             
10297         }
10298         
10299     }
10300     
10301 });
10302
10303 /*
10304  * Based on:
10305  * Ext JS Library 1.1.1
10306  * Copyright(c) 2006-2007, Ext JS, LLC.
10307  *
10308  * Originally Released Under LGPL - original licence link has changed is not relivant.
10309  *
10310  * Fork - LGPL
10311  * <script type="text/javascript">
10312  */
10313 /**
10314  * @class Roo.form.VTypes
10315  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10316  * @singleton
10317  */
10318 Roo.form.VTypes = function(){
10319     // closure these in so they are only created once.
10320     var alpha = /^[a-zA-Z_]+$/;
10321     var alphanum = /^[a-zA-Z0-9_]+$/;
10322     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10323     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10324
10325     // All these messages and functions are configurable
10326     return {
10327         /**
10328          * The function used to validate email addresses
10329          * @param {String} value The email address
10330          */
10331         'email' : function(v){
10332             return email.test(v);
10333         },
10334         /**
10335          * The error text to display when the email validation function returns false
10336          * @type String
10337          */
10338         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10339         /**
10340          * The keystroke filter mask to be applied on email input
10341          * @type RegExp
10342          */
10343         'emailMask' : /[a-z0-9_\.\-@]/i,
10344
10345         /**
10346          * The function used to validate URLs
10347          * @param {String} value The URL
10348          */
10349         'url' : function(v){
10350             return url.test(v);
10351         },
10352         /**
10353          * The error text to display when the url validation function returns false
10354          * @type String
10355          */
10356         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10357         
10358         /**
10359          * The function used to validate alpha values
10360          * @param {String} value The value
10361          */
10362         'alpha' : function(v){
10363             return alpha.test(v);
10364         },
10365         /**
10366          * The error text to display when the alpha validation function returns false
10367          * @type String
10368          */
10369         'alphaText' : 'This field should only contain letters and _',
10370         /**
10371          * The keystroke filter mask to be applied on alpha input
10372          * @type RegExp
10373          */
10374         'alphaMask' : /[a-z_]/i,
10375
10376         /**
10377          * The function used to validate alphanumeric values
10378          * @param {String} value The value
10379          */
10380         'alphanum' : function(v){
10381             return alphanum.test(v);
10382         },
10383         /**
10384          * The error text to display when the alphanumeric validation function returns false
10385          * @type String
10386          */
10387         'alphanumText' : 'This field should only contain letters, numbers and _',
10388         /**
10389          * The keystroke filter mask to be applied on alphanumeric input
10390          * @type RegExp
10391          */
10392         'alphanumMask' : /[a-z0-9_]/i
10393     };
10394 }();/*
10395  * - LGPL
10396  *
10397  * Input
10398  * 
10399  */
10400
10401 /**
10402  * @class Roo.bootstrap.Input
10403  * @extends Roo.bootstrap.Component
10404  * Bootstrap Input class
10405  * @cfg {Boolean} disabled is it disabled
10406  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10407  * @cfg {String} name name of the input
10408  * @cfg {string} fieldLabel - the label associated
10409  * @cfg {string} placeholder - placeholder to put in text.
10410  * @cfg {string}  before - input group add on before
10411  * @cfg {string} after - input group add on after
10412  * @cfg {string} size - (lg|sm) or leave empty..
10413  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10414  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10415  * @cfg {Number} md colspan out of 12 for computer-sized screens
10416  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10417  * @cfg {string} value default value of the input
10418  * @cfg {Number} labelWidth set the width of label 
10419  * @cfg {Number} labellg set the width of label (1-12)
10420  * @cfg {Number} labelmd set the width of label (1-12)
10421  * @cfg {Number} labelsm set the width of label (1-12)
10422  * @cfg {Number} labelxs set the width of label (1-12)
10423  * @cfg {String} labelAlign (top|left)
10424  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10425  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10426  * @cfg {String} indicatorpos (left|right) default left
10427  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10428  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10429  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10430
10431  * @cfg {String} align (left|center|right) Default left
10432  * @cfg {Boolean} forceFeedback (true|false) Default false
10433  * 
10434  * @constructor
10435  * Create a new Input
10436  * @param {Object} config The config object
10437  */
10438
10439 Roo.bootstrap.Input = function(config){
10440     
10441     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10442     
10443     this.addEvents({
10444         /**
10445          * @event focus
10446          * Fires when this field receives input focus.
10447          * @param {Roo.form.Field} this
10448          */
10449         focus : true,
10450         /**
10451          * @event blur
10452          * Fires when this field loses input focus.
10453          * @param {Roo.form.Field} this
10454          */
10455         blur : true,
10456         /**
10457          * @event specialkey
10458          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10459          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10460          * @param {Roo.form.Field} this
10461          * @param {Roo.EventObject} e The event object
10462          */
10463         specialkey : true,
10464         /**
10465          * @event change
10466          * Fires just before the field blurs if the field value has changed.
10467          * @param {Roo.form.Field} this
10468          * @param {Mixed} newValue The new value
10469          * @param {Mixed} oldValue The original value
10470          */
10471         change : true,
10472         /**
10473          * @event invalid
10474          * Fires after the field has been marked as invalid.
10475          * @param {Roo.form.Field} this
10476          * @param {String} msg The validation message
10477          */
10478         invalid : true,
10479         /**
10480          * @event valid
10481          * Fires after the field has been validated with no errors.
10482          * @param {Roo.form.Field} this
10483          */
10484         valid : true,
10485          /**
10486          * @event keyup
10487          * Fires after the key up
10488          * @param {Roo.form.Field} this
10489          * @param {Roo.EventObject}  e The event Object
10490          */
10491         keyup : true
10492     });
10493 };
10494
10495 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10496      /**
10497      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10498       automatic validation (defaults to "keyup").
10499      */
10500     validationEvent : "keyup",
10501      /**
10502      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10503      */
10504     validateOnBlur : true,
10505     /**
10506      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10507      */
10508     validationDelay : 250,
10509      /**
10510      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10511      */
10512     focusClass : "x-form-focus",  // not needed???
10513     
10514        
10515     /**
10516      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10517      */
10518     invalidClass : "has-warning",
10519     
10520     /**
10521      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     validClass : "has-success",
10524     
10525     /**
10526      * @cfg {Boolean} hasFeedback (true|false) default true
10527      */
10528     hasFeedback : true,
10529     
10530     /**
10531      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10532      */
10533     invalidFeedbackClass : "glyphicon-warning-sign",
10534     
10535     /**
10536      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     validFeedbackClass : "glyphicon-ok",
10539     
10540     /**
10541      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10542      */
10543     selectOnFocus : false,
10544     
10545      /**
10546      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10547      */
10548     maskRe : null,
10549        /**
10550      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10551      */
10552     vtype : null,
10553     
10554       /**
10555      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10556      */
10557     disableKeyFilter : false,
10558     
10559        /**
10560      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10561      */
10562     disabled : false,
10563      /**
10564      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10565      */
10566     allowBlank : true,
10567     /**
10568      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10569      */
10570     blankText : "Please complete this mandatory field",
10571     
10572      /**
10573      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10574      */
10575     minLength : 0,
10576     /**
10577      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10578      */
10579     maxLength : Number.MAX_VALUE,
10580     /**
10581      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10582      */
10583     minLengthText : "The minimum length for this field is {0}",
10584     /**
10585      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10586      */
10587     maxLengthText : "The maximum length for this field is {0}",
10588   
10589     
10590     /**
10591      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10592      * If available, this function will be called only after the basic validators all return true, and will be passed the
10593      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10594      */
10595     validator : null,
10596     /**
10597      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10598      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10599      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10600      */
10601     regex : null,
10602     /**
10603      * @cfg {String} regexText -- Depricated - use Invalid Text
10604      */
10605     regexText : "",
10606     
10607     /**
10608      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10609      */
10610     invalidText : "",
10611     
10612     
10613     
10614     autocomplete: false,
10615     
10616     
10617     fieldLabel : '',
10618     inputType : 'text',
10619     
10620     name : false,
10621     placeholder: false,
10622     before : false,
10623     after : false,
10624     size : false,
10625     hasFocus : false,
10626     preventMark: false,
10627     isFormField : true,
10628     value : '',
10629     labelWidth : 2,
10630     labelAlign : false,
10631     readOnly : false,
10632     align : false,
10633     formatedValue : false,
10634     forceFeedback : false,
10635     
10636     indicatorpos : 'left',
10637     
10638     labellg : 0,
10639     labelmd : 0,
10640     labelsm : 0,
10641     labelxs : 0,
10642     
10643     capture : '',
10644     accept : '',
10645     
10646     parentLabelAlign : function()
10647     {
10648         var parent = this;
10649         while (parent.parent()) {
10650             parent = parent.parent();
10651             if (typeof(parent.labelAlign) !='undefined') {
10652                 return parent.labelAlign;
10653             }
10654         }
10655         return 'left';
10656         
10657     },
10658     
10659     getAutoCreate : function()
10660     {
10661         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10662         
10663         var id = Roo.id();
10664         
10665         var cfg = {};
10666         
10667         if(this.inputType != 'hidden'){
10668             cfg.cls = 'form-group' //input-group
10669         }
10670         
10671         var input =  {
10672             tag: 'input',
10673             id : id,
10674             type : this.inputType,
10675             value : this.value,
10676             cls : 'form-control',
10677             placeholder : this.placeholder || '',
10678             autocomplete : this.autocomplete || 'new-password'
10679         };
10680         if (this.inputType == 'file') {
10681             input.style = 'overflow:hidden'; // why not in CSS?
10682         }
10683         
10684         if(this.capture.length){
10685             input.capture = this.capture;
10686         }
10687         
10688         if(this.accept.length){
10689             input.accept = this.accept + "/*";
10690         }
10691         
10692         if(this.align){
10693             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10694         }
10695         
10696         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10697             input.maxLength = this.maxLength;
10698         }
10699         
10700         if (this.disabled) {
10701             input.disabled=true;
10702         }
10703         
10704         if (this.readOnly) {
10705             input.readonly=true;
10706         }
10707         
10708         if (this.name) {
10709             input.name = this.name;
10710         }
10711         
10712         if (this.size) {
10713             input.cls += ' input-' + this.size;
10714         }
10715         
10716         var settings=this;
10717         ['xs','sm','md','lg'].map(function(size){
10718             if (settings[size]) {
10719                 cfg.cls += ' col-' + size + '-' + settings[size];
10720             }
10721         });
10722         
10723         var inputblock = input;
10724         
10725         var feedback = {
10726             tag: 'span',
10727             cls: 'glyphicon form-control-feedback'
10728         };
10729             
10730         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10731             
10732             inputblock = {
10733                 cls : 'has-feedback',
10734                 cn :  [
10735                     input,
10736                     feedback
10737                 ] 
10738             };  
10739         }
10740         
10741         if (this.before || this.after) {
10742             
10743             inputblock = {
10744                 cls : 'input-group',
10745                 cn :  [] 
10746             };
10747             
10748             if (this.before && typeof(this.before) == 'string') {
10749                 
10750                 inputblock.cn.push({
10751                     tag :'span',
10752                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10753                     html : this.before
10754                 });
10755             }
10756             if (this.before && typeof(this.before) == 'object') {
10757                 this.before = Roo.factory(this.before);
10758                 
10759                 inputblock.cn.push({
10760                     tag :'span',
10761                     cls : 'roo-input-before input-group-prepend   input-group-' +
10762                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10763                 });
10764             }
10765             
10766             inputblock.cn.push(input);
10767             
10768             if (this.after && typeof(this.after) == 'string') {
10769                 inputblock.cn.push({
10770                     tag :'span',
10771                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10772                     html : this.after
10773                 });
10774             }
10775             if (this.after && typeof(this.after) == 'object') {
10776                 this.after = Roo.factory(this.after);
10777                 
10778                 inputblock.cn.push({
10779                     tag :'span',
10780                     cls : 'roo-input-after input-group-append  input-group-' +
10781                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10782                 });
10783             }
10784             
10785             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10786                 inputblock.cls += ' has-feedback';
10787                 inputblock.cn.push(feedback);
10788             }
10789         };
10790         var indicator = {
10791             tag : 'i',
10792             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10793             tooltip : 'This field is required'
10794         };
10795         if (this.allowBlank ) {
10796             indicator.style = this.allowBlank ? ' display:none' : '';
10797         }
10798         if (align ==='left' && this.fieldLabel.length) {
10799             
10800             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10801             
10802             cfg.cn = [
10803                 indicator,
10804                 {
10805                     tag: 'label',
10806                     'for' :  id,
10807                     cls : 'control-label col-form-label',
10808                     html : this.fieldLabel
10809
10810                 },
10811                 {
10812                     cls : "", 
10813                     cn: [
10814                         inputblock
10815                     ]
10816                 }
10817             ];
10818             
10819             var labelCfg = cfg.cn[1];
10820             var contentCfg = cfg.cn[2];
10821             
10822             if(this.indicatorpos == 'right'){
10823                 cfg.cn = [
10824                     {
10825                         tag: 'label',
10826                         'for' :  id,
10827                         cls : 'control-label col-form-label',
10828                         cn : [
10829                             {
10830                                 tag : 'span',
10831                                 html : this.fieldLabel
10832                             },
10833                             indicator
10834                         ]
10835                     },
10836                     {
10837                         cls : "",
10838                         cn: [
10839                             inputblock
10840                         ]
10841                     }
10842
10843                 ];
10844                 
10845                 labelCfg = cfg.cn[0];
10846                 contentCfg = cfg.cn[1];
10847             
10848             }
10849             
10850             if(this.labelWidth > 12){
10851                 labelCfg.style = "width: " + this.labelWidth + 'px';
10852             }
10853             
10854             if(this.labelWidth < 13 && this.labelmd == 0){
10855                 this.labelmd = this.labelWidth;
10856             }
10857             
10858             if(this.labellg > 0){
10859                 labelCfg.cls += ' col-lg-' + this.labellg;
10860                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10861             }
10862             
10863             if(this.labelmd > 0){
10864                 labelCfg.cls += ' col-md-' + this.labelmd;
10865                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10866             }
10867             
10868             if(this.labelsm > 0){
10869                 labelCfg.cls += ' col-sm-' + this.labelsm;
10870                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10871             }
10872             
10873             if(this.labelxs > 0){
10874                 labelCfg.cls += ' col-xs-' + this.labelxs;
10875                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10876             }
10877             
10878             
10879         } else if ( this.fieldLabel.length) {
10880                 
10881             
10882             
10883             cfg.cn = [
10884                 {
10885                     tag : 'i',
10886                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10887                     tooltip : 'This field is required',
10888                     style : this.allowBlank ? ' display:none' : '' 
10889                 },
10890                 {
10891                     tag: 'label',
10892                    //cls : 'input-group-addon',
10893                     html : this.fieldLabel
10894
10895                 },
10896
10897                inputblock
10898
10899            ];
10900            
10901            if(this.indicatorpos == 'right'){
10902        
10903                 cfg.cn = [
10904                     {
10905                         tag: 'label',
10906                        //cls : 'input-group-addon',
10907                         html : this.fieldLabel
10908
10909                     },
10910                     {
10911                         tag : 'i',
10912                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10913                         tooltip : 'This field is required',
10914                         style : this.allowBlank ? ' display:none' : '' 
10915                     },
10916
10917                    inputblock
10918
10919                ];
10920
10921             }
10922
10923         } else {
10924             
10925             cfg.cn = [
10926
10927                     inputblock
10928
10929             ];
10930                 
10931                 
10932         };
10933         
10934         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10935            cfg.cls += ' navbar-form';
10936         }
10937         
10938         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10939             // on BS4 we do this only if not form 
10940             cfg.cls += ' navbar-form';
10941             cfg.tag = 'li';
10942         }
10943         
10944         return cfg;
10945         
10946     },
10947     /**
10948      * return the real input element.
10949      */
10950     inputEl: function ()
10951     {
10952         return this.el.select('input.form-control',true).first();
10953     },
10954     
10955     tooltipEl : function()
10956     {
10957         return this.inputEl();
10958     },
10959     
10960     indicatorEl : function()
10961     {
10962         if (Roo.bootstrap.version == 4) {
10963             return false; // not enabled in v4 yet.
10964         }
10965         
10966         var indicator = this.el.select('i.roo-required-indicator',true).first();
10967         
10968         if(!indicator){
10969             return false;
10970         }
10971         
10972         return indicator;
10973         
10974     },
10975     
10976     setDisabled : function(v)
10977     {
10978         var i  = this.inputEl().dom;
10979         if (!v) {
10980             i.removeAttribute('disabled');
10981             return;
10982             
10983         }
10984         i.setAttribute('disabled','true');
10985     },
10986     initEvents : function()
10987     {
10988           
10989         this.inputEl().on("keydown" , this.fireKey,  this);
10990         this.inputEl().on("focus", this.onFocus,  this);
10991         this.inputEl().on("blur", this.onBlur,  this);
10992         
10993         this.inputEl().relayEvent('keyup', this);
10994         
10995         this.indicator = this.indicatorEl();
10996         
10997         if(this.indicator){
10998             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10999         }
11000  
11001         // reference to original value for reset
11002         this.originalValue = this.getValue();
11003         //Roo.form.TextField.superclass.initEvents.call(this);
11004         if(this.validationEvent == 'keyup'){
11005             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11006             this.inputEl().on('keyup', this.filterValidation, this);
11007         }
11008         else if(this.validationEvent !== false){
11009             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11010         }
11011         
11012         if(this.selectOnFocus){
11013             this.on("focus", this.preFocus, this);
11014             
11015         }
11016         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11017             this.inputEl().on("keypress", this.filterKeys, this);
11018         } else {
11019             this.inputEl().relayEvent('keypress', this);
11020         }
11021        /* if(this.grow){
11022             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11023             this.el.on("click", this.autoSize,  this);
11024         }
11025         */
11026         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11027             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11028         }
11029         
11030         if (typeof(this.before) == 'object') {
11031             this.before.render(this.el.select('.roo-input-before',true).first());
11032         }
11033         if (typeof(this.after) == 'object') {
11034             this.after.render(this.el.select('.roo-input-after',true).first());
11035         }
11036         
11037         this.inputEl().on('change', this.onChange, this);
11038         
11039     },
11040     filterValidation : function(e){
11041         if(!e.isNavKeyPress()){
11042             this.validationTask.delay(this.validationDelay);
11043         }
11044     },
11045      /**
11046      * Validates the field value
11047      * @return {Boolean} True if the value is valid, else false
11048      */
11049     validate : function(){
11050         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11051         if(this.disabled || this.validateValue(this.getRawValue())){
11052             this.markValid();
11053             return true;
11054         }
11055         
11056         this.markInvalid();
11057         return false;
11058     },
11059     
11060     
11061     /**
11062      * Validates a value according to the field's validation rules and marks the field as invalid
11063      * if the validation fails
11064      * @param {Mixed} value The value to validate
11065      * @return {Boolean} True if the value is valid, else false
11066      */
11067     validateValue : function(value)
11068     {
11069         if(this.getVisibilityEl().hasClass('hidden')){
11070             return true;
11071         }
11072         
11073         if(value.length < 1)  { // if it's blank
11074             if(this.allowBlank){
11075                 return true;
11076             }
11077             return false;
11078         }
11079         
11080         if(value.length < this.minLength){
11081             return false;
11082         }
11083         if(value.length > this.maxLength){
11084             return false;
11085         }
11086         if(this.vtype){
11087             var vt = Roo.form.VTypes;
11088             if(!vt[this.vtype](value, this)){
11089                 return false;
11090             }
11091         }
11092         if(typeof this.validator == "function"){
11093             var msg = this.validator(value);
11094             if(msg !== true){
11095                 return false;
11096             }
11097             if (typeof(msg) == 'string') {
11098                 this.invalidText = msg;
11099             }
11100         }
11101         
11102         if(this.regex && !this.regex.test(value)){
11103             return false;
11104         }
11105         
11106         return true;
11107     },
11108     
11109      // private
11110     fireKey : function(e){
11111         //Roo.log('field ' + e.getKey());
11112         if(e.isNavKeyPress()){
11113             this.fireEvent("specialkey", this, e);
11114         }
11115     },
11116     focus : function (selectText){
11117         if(this.rendered){
11118             this.inputEl().focus();
11119             if(selectText === true){
11120                 this.inputEl().dom.select();
11121             }
11122         }
11123         return this;
11124     } ,
11125     
11126     onFocus : function(){
11127         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11128            // this.el.addClass(this.focusClass);
11129         }
11130         if(!this.hasFocus){
11131             this.hasFocus = true;
11132             this.startValue = this.getValue();
11133             this.fireEvent("focus", this);
11134         }
11135     },
11136     
11137     beforeBlur : Roo.emptyFn,
11138
11139     
11140     // private
11141     onBlur : function(){
11142         this.beforeBlur();
11143         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11144             //this.el.removeClass(this.focusClass);
11145         }
11146         this.hasFocus = false;
11147         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11148             this.validate();
11149         }
11150         var v = this.getValue();
11151         if(String(v) !== String(this.startValue)){
11152             this.fireEvent('change', this, v, this.startValue);
11153         }
11154         this.fireEvent("blur", this);
11155     },
11156     
11157     onChange : function(e)
11158     {
11159         var v = this.getValue();
11160         if(String(v) !== String(this.startValue)){
11161             this.fireEvent('change', this, v, this.startValue);
11162         }
11163         
11164     },
11165     
11166     /**
11167      * Resets the current field value to the originally loaded value and clears any validation messages
11168      */
11169     reset : function(){
11170         this.setValue(this.originalValue);
11171         this.validate();
11172     },
11173      /**
11174      * Returns the name of the field
11175      * @return {Mixed} name The name field
11176      */
11177     getName: function(){
11178         return this.name;
11179     },
11180      /**
11181      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11182      * @return {Mixed} value The field value
11183      */
11184     getValue : function(){
11185         
11186         var v = this.inputEl().getValue();
11187         
11188         return v;
11189     },
11190     /**
11191      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11192      * @return {Mixed} value The field value
11193      */
11194     getRawValue : function(){
11195         var v = this.inputEl().getValue();
11196         
11197         return v;
11198     },
11199     
11200     /**
11201      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11202      * @param {Mixed} value The value to set
11203      */
11204     setRawValue : function(v){
11205         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11206     },
11207     
11208     selectText : function(start, end){
11209         var v = this.getRawValue();
11210         if(v.length > 0){
11211             start = start === undefined ? 0 : start;
11212             end = end === undefined ? v.length : end;
11213             var d = this.inputEl().dom;
11214             if(d.setSelectionRange){
11215                 d.setSelectionRange(start, end);
11216             }else if(d.createTextRange){
11217                 var range = d.createTextRange();
11218                 range.moveStart("character", start);
11219                 range.moveEnd("character", v.length-end);
11220                 range.select();
11221             }
11222         }
11223     },
11224     
11225     /**
11226      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11227      * @param {Mixed} value The value to set
11228      */
11229     setValue : function(v){
11230         this.value = v;
11231         if(this.rendered){
11232             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11233             this.validate();
11234         }
11235     },
11236     
11237     /*
11238     processValue : function(value){
11239         if(this.stripCharsRe){
11240             var newValue = value.replace(this.stripCharsRe, '');
11241             if(newValue !== value){
11242                 this.setRawValue(newValue);
11243                 return newValue;
11244             }
11245         }
11246         return value;
11247     },
11248   */
11249     preFocus : function(){
11250         
11251         if(this.selectOnFocus){
11252             this.inputEl().dom.select();
11253         }
11254     },
11255     filterKeys : function(e){
11256         var k = e.getKey();
11257         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11258             return;
11259         }
11260         var c = e.getCharCode(), cc = String.fromCharCode(c);
11261         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11262             return;
11263         }
11264         if(!this.maskRe.test(cc)){
11265             e.stopEvent();
11266         }
11267     },
11268      /**
11269      * Clear any invalid styles/messages for this field
11270      */
11271     clearInvalid : function(){
11272         
11273         if(!this.el || this.preventMark){ // not rendered
11274             return;
11275         }
11276         
11277         
11278         this.el.removeClass([this.invalidClass, 'is-invalid']);
11279         
11280         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11281             
11282             var feedback = this.el.select('.form-control-feedback', true).first();
11283             
11284             if(feedback){
11285                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11286             }
11287             
11288         }
11289         
11290         if(this.indicator){
11291             this.indicator.removeClass('visible');
11292             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11293         }
11294         
11295         this.fireEvent('valid', this);
11296     },
11297     
11298      /**
11299      * Mark this field as valid
11300      */
11301     markValid : function()
11302     {
11303         if(!this.el  || this.preventMark){ // not rendered...
11304             return;
11305         }
11306         
11307         this.el.removeClass([this.invalidClass, this.validClass]);
11308         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11309
11310         var feedback = this.el.select('.form-control-feedback', true).first();
11311             
11312         if(feedback){
11313             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11314         }
11315         
11316         if(this.indicator){
11317             this.indicator.removeClass('visible');
11318             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11319         }
11320         
11321         if(this.disabled){
11322             return;
11323         }
11324         
11325            
11326         if(this.allowBlank && !this.getRawValue().length){
11327             return;
11328         }
11329         if (Roo.bootstrap.version == 3) {
11330             this.el.addClass(this.validClass);
11331         } else {
11332             this.inputEl().addClass('is-valid');
11333         }
11334
11335         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11336             
11337             var feedback = this.el.select('.form-control-feedback', true).first();
11338             
11339             if(feedback){
11340                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11341                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11342             }
11343             
11344         }
11345         
11346         this.fireEvent('valid', this);
11347     },
11348     
11349      /**
11350      * Mark this field as invalid
11351      * @param {String} msg The validation message
11352      */
11353     markInvalid : function(msg)
11354     {
11355         if(!this.el  || this.preventMark){ // not rendered
11356             return;
11357         }
11358         
11359         this.el.removeClass([this.invalidClass, this.validClass]);
11360         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11361         
11362         var feedback = this.el.select('.form-control-feedback', true).first();
11363             
11364         if(feedback){
11365             this.el.select('.form-control-feedback', true).first().removeClass(
11366                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11367         }
11368
11369         if(this.disabled){
11370             return;
11371         }
11372         
11373         if(this.allowBlank && !this.getRawValue().length){
11374             return;
11375         }
11376         
11377         if(this.indicator){
11378             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11379             this.indicator.addClass('visible');
11380         }
11381         if (Roo.bootstrap.version == 3) {
11382             this.el.addClass(this.invalidClass);
11383         } else {
11384             this.inputEl().addClass('is-invalid');
11385         }
11386         
11387         
11388         
11389         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11390             
11391             var feedback = this.el.select('.form-control-feedback', true).first();
11392             
11393             if(feedback){
11394                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11395                 
11396                 if(this.getValue().length || this.forceFeedback){
11397                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11398                 }
11399                 
11400             }
11401             
11402         }
11403         
11404         this.fireEvent('invalid', this, msg);
11405     },
11406     // private
11407     SafariOnKeyDown : function(event)
11408     {
11409         // this is a workaround for a password hang bug on chrome/ webkit.
11410         if (this.inputEl().dom.type != 'password') {
11411             return;
11412         }
11413         
11414         var isSelectAll = false;
11415         
11416         if(this.inputEl().dom.selectionEnd > 0){
11417             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11418         }
11419         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11420             event.preventDefault();
11421             this.setValue('');
11422             return;
11423         }
11424         
11425         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11426             
11427             event.preventDefault();
11428             // this is very hacky as keydown always get's upper case.
11429             //
11430             var cc = String.fromCharCode(event.getCharCode());
11431             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11432             
11433         }
11434     },
11435     adjustWidth : function(tag, w){
11436         tag = tag.toLowerCase();
11437         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11438             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11439                 if(tag == 'input'){
11440                     return w + 2;
11441                 }
11442                 if(tag == 'textarea'){
11443                     return w-2;
11444                 }
11445             }else if(Roo.isOpera){
11446                 if(tag == 'input'){
11447                     return w + 2;
11448                 }
11449                 if(tag == 'textarea'){
11450                     return w-2;
11451                 }
11452             }
11453         }
11454         return w;
11455     },
11456     
11457     setFieldLabel : function(v)
11458     {
11459         if(!this.rendered){
11460             return;
11461         }
11462         
11463         if(this.indicatorEl()){
11464             var ar = this.el.select('label > span',true);
11465             
11466             if (ar.elements.length) {
11467                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11468                 this.fieldLabel = v;
11469                 return;
11470             }
11471             
11472             var br = this.el.select('label',true);
11473             
11474             if(br.elements.length) {
11475                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             Roo.log('Cannot Found any of label > span || label in input');
11481             return;
11482         }
11483         
11484         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11485         this.fieldLabel = v;
11486         
11487         
11488     }
11489 });
11490
11491  
11492 /*
11493  * - LGPL
11494  *
11495  * Input
11496  * 
11497  */
11498
11499 /**
11500  * @class Roo.bootstrap.TextArea
11501  * @extends Roo.bootstrap.Input
11502  * Bootstrap TextArea class
11503  * @cfg {Number} cols Specifies the visible width of a text area
11504  * @cfg {Number} rows Specifies the visible number of lines in a text area
11505  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11506  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11507  * @cfg {string} html text
11508  * 
11509  * @constructor
11510  * Create a new TextArea
11511  * @param {Object} config The config object
11512  */
11513
11514 Roo.bootstrap.TextArea = function(config){
11515     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11516    
11517 };
11518
11519 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11520      
11521     cols : false,
11522     rows : 5,
11523     readOnly : false,
11524     warp : 'soft',
11525     resize : false,
11526     value: false,
11527     html: false,
11528     
11529     getAutoCreate : function(){
11530         
11531         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11532         
11533         var id = Roo.id();
11534         
11535         var cfg = {};
11536         
11537         if(this.inputType != 'hidden'){
11538             cfg.cls = 'form-group' //input-group
11539         }
11540         
11541         var input =  {
11542             tag: 'textarea',
11543             id : id,
11544             warp : this.warp,
11545             rows : this.rows,
11546             value : this.value || '',
11547             html: this.html || '',
11548             cls : 'form-control',
11549             placeholder : this.placeholder || '' 
11550             
11551         };
11552         
11553         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11554             input.maxLength = this.maxLength;
11555         }
11556         
11557         if(this.resize){
11558             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11559         }
11560         
11561         if(this.cols){
11562             input.cols = this.cols;
11563         }
11564         
11565         if (this.readOnly) {
11566             input.readonly = true;
11567         }
11568         
11569         if (this.name) {
11570             input.name = this.name;
11571         }
11572         
11573         if (this.size) {
11574             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11575         }
11576         
11577         var settings=this;
11578         ['xs','sm','md','lg'].map(function(size){
11579             if (settings[size]) {
11580                 cfg.cls += ' col-' + size + '-' + settings[size];
11581             }
11582         });
11583         
11584         var inputblock = input;
11585         
11586         if(this.hasFeedback && !this.allowBlank){
11587             
11588             var feedback = {
11589                 tag: 'span',
11590                 cls: 'glyphicon form-control-feedback'
11591             };
11592
11593             inputblock = {
11594                 cls : 'has-feedback',
11595                 cn :  [
11596                     input,
11597                     feedback
11598                 ] 
11599             };  
11600         }
11601         
11602         
11603         if (this.before || this.after) {
11604             
11605             inputblock = {
11606                 cls : 'input-group',
11607                 cn :  [] 
11608             };
11609             if (this.before) {
11610                 inputblock.cn.push({
11611                     tag :'span',
11612                     cls : 'input-group-addon',
11613                     html : this.before
11614                 });
11615             }
11616             
11617             inputblock.cn.push(input);
11618             
11619             if(this.hasFeedback && !this.allowBlank){
11620                 inputblock.cls += ' has-feedback';
11621                 inputblock.cn.push(feedback);
11622             }
11623             
11624             if (this.after) {
11625                 inputblock.cn.push({
11626                     tag :'span',
11627                     cls : 'input-group-addon',
11628                     html : this.after
11629                 });
11630             }
11631             
11632         }
11633         
11634         if (align ==='left' && this.fieldLabel.length) {
11635             cfg.cn = [
11636                 {
11637                     tag: 'label',
11638                     'for' :  id,
11639                     cls : 'control-label',
11640                     html : this.fieldLabel
11641                 },
11642                 {
11643                     cls : "",
11644                     cn: [
11645                         inputblock
11646                     ]
11647                 }
11648
11649             ];
11650             
11651             if(this.labelWidth > 12){
11652                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11653             }
11654
11655             if(this.labelWidth < 13 && this.labelmd == 0){
11656                 this.labelmd = this.labelWidth;
11657             }
11658
11659             if(this.labellg > 0){
11660                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11661                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11662             }
11663
11664             if(this.labelmd > 0){
11665                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11666                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11667             }
11668
11669             if(this.labelsm > 0){
11670                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11671                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11672             }
11673
11674             if(this.labelxs > 0){
11675                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11676                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11677             }
11678             
11679         } else if ( this.fieldLabel.length) {
11680             cfg.cn = [
11681
11682                {
11683                    tag: 'label',
11684                    //cls : 'input-group-addon',
11685                    html : this.fieldLabel
11686
11687                },
11688
11689                inputblock
11690
11691            ];
11692
11693         } else {
11694
11695             cfg.cn = [
11696
11697                 inputblock
11698
11699             ];
11700                 
11701         }
11702         
11703         if (this.disabled) {
11704             input.disabled=true;
11705         }
11706         
11707         return cfg;
11708         
11709     },
11710     /**
11711      * return the real textarea element.
11712      */
11713     inputEl: function ()
11714     {
11715         return this.el.select('textarea.form-control',true).first();
11716     },
11717     
11718     /**
11719      * Clear any invalid styles/messages for this field
11720      */
11721     clearInvalid : function()
11722     {
11723         
11724         if(!this.el || this.preventMark){ // not rendered
11725             return;
11726         }
11727         
11728         var label = this.el.select('label', true).first();
11729         var icon = this.el.select('i.fa-star', true).first();
11730         
11731         if(label && icon){
11732             icon.remove();
11733         }
11734         this.el.removeClass( this.validClass);
11735         this.inputEl().removeClass('is-invalid');
11736          
11737         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11738             
11739             var feedback = this.el.select('.form-control-feedback', true).first();
11740             
11741             if(feedback){
11742                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11743             }
11744             
11745         }
11746         
11747         this.fireEvent('valid', this);
11748     },
11749     
11750      /**
11751      * Mark this field as valid
11752      */
11753     markValid : function()
11754     {
11755         if(!this.el  || this.preventMark){ // not rendered
11756             return;
11757         }
11758         
11759         this.el.removeClass([this.invalidClass, this.validClass]);
11760         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11761         
11762         var feedback = this.el.select('.form-control-feedback', true).first();
11763             
11764         if(feedback){
11765             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11766         }
11767
11768         if(this.disabled || this.allowBlank){
11769             return;
11770         }
11771         
11772         var label = this.el.select('label', true).first();
11773         var icon = this.el.select('i.fa-star', true).first();
11774         
11775         if(label && icon){
11776             icon.remove();
11777         }
11778         if (Roo.bootstrap.version == 3) {
11779             this.el.addClass(this.validClass);
11780         } else {
11781             this.inputEl().addClass('is-valid');
11782         }
11783         
11784         
11785         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11786             
11787             var feedback = this.el.select('.form-control-feedback', true).first();
11788             
11789             if(feedback){
11790                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11791                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11792             }
11793             
11794         }
11795         
11796         this.fireEvent('valid', this);
11797     },
11798     
11799      /**
11800      * Mark this field as invalid
11801      * @param {String} msg The validation message
11802      */
11803     markInvalid : function(msg)
11804     {
11805         if(!this.el  || this.preventMark){ // not rendered
11806             return;
11807         }
11808         
11809         this.el.removeClass([this.invalidClass, this.validClass]);
11810         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11811         
11812         var feedback = this.el.select('.form-control-feedback', true).first();
11813             
11814         if(feedback){
11815             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11816         }
11817
11818         if(this.disabled || this.allowBlank){
11819             return;
11820         }
11821         
11822         var label = this.el.select('label', true).first();
11823         var icon = this.el.select('i.fa-star', true).first();
11824         
11825         if(!this.getValue().length && label && !icon){
11826             this.el.createChild({
11827                 tag : 'i',
11828                 cls : 'text-danger fa fa-lg fa-star',
11829                 tooltip : 'This field is required',
11830                 style : 'margin-right:5px;'
11831             }, label, true);
11832         }
11833         
11834         if (Roo.bootstrap.version == 3) {
11835             this.el.addClass(this.invalidClass);
11836         } else {
11837             this.inputEl().addClass('is-invalid');
11838         }
11839         
11840         // fixme ... this may be depricated need to test..
11841         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11842             
11843             var feedback = this.el.select('.form-control-feedback', true).first();
11844             
11845             if(feedback){
11846                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11847                 
11848                 if(this.getValue().length || this.forceFeedback){
11849                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11850                 }
11851                 
11852             }
11853             
11854         }
11855         
11856         this.fireEvent('invalid', this, msg);
11857     }
11858 });
11859
11860  
11861 /*
11862  * - LGPL
11863  *
11864  * trigger field - base class for combo..
11865  * 
11866  */
11867  
11868 /**
11869  * @class Roo.bootstrap.TriggerField
11870  * @extends Roo.bootstrap.Input
11871  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11872  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11873  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11874  * for which you can provide a custom implementation.  For example:
11875  * <pre><code>
11876 var trigger = new Roo.bootstrap.TriggerField();
11877 trigger.onTriggerClick = myTriggerFn;
11878 trigger.applyTo('my-field');
11879 </code></pre>
11880  *
11881  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11882  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11883  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11884  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11885  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11886
11887  * @constructor
11888  * Create a new TriggerField.
11889  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11890  * to the base TextField)
11891  */
11892 Roo.bootstrap.TriggerField = function(config){
11893     this.mimicing = false;
11894     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11895 };
11896
11897 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11898     /**
11899      * @cfg {String} triggerClass A CSS class to apply to the trigger
11900      */
11901      /**
11902      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11903      */
11904     hideTrigger:false,
11905
11906     /**
11907      * @cfg {Boolean} removable (true|false) special filter default false
11908      */
11909     removable : false,
11910     
11911     /** @cfg {Boolean} grow @hide */
11912     /** @cfg {Number} growMin @hide */
11913     /** @cfg {Number} growMax @hide */
11914
11915     /**
11916      * @hide 
11917      * @method
11918      */
11919     autoSize: Roo.emptyFn,
11920     // private
11921     monitorTab : true,
11922     // private
11923     deferHeight : true,
11924
11925     
11926     actionMode : 'wrap',
11927     
11928     caret : false,
11929     
11930     
11931     getAutoCreate : function(){
11932        
11933         var align = this.labelAlign || this.parentLabelAlign();
11934         
11935         var id = Roo.id();
11936         
11937         var cfg = {
11938             cls: 'form-group' //input-group
11939         };
11940         
11941         
11942         var input =  {
11943             tag: 'input',
11944             id : id,
11945             type : this.inputType,
11946             cls : 'form-control',
11947             autocomplete: 'new-password',
11948             placeholder : this.placeholder || '' 
11949             
11950         };
11951         if (this.name) {
11952             input.name = this.name;
11953         }
11954         if (this.size) {
11955             input.cls += ' input-' + this.size;
11956         }
11957         
11958         if (this.disabled) {
11959             input.disabled=true;
11960         }
11961         
11962         var inputblock = input;
11963         
11964         if(this.hasFeedback && !this.allowBlank){
11965             
11966             var feedback = {
11967                 tag: 'span',
11968                 cls: 'glyphicon form-control-feedback'
11969             };
11970             
11971             if(this.removable && !this.editable  ){
11972                 inputblock = {
11973                     cls : 'has-feedback',
11974                     cn :  [
11975                         inputblock,
11976                         {
11977                             tag: 'button',
11978                             html : 'x',
11979                             cls : 'roo-combo-removable-btn close'
11980                         },
11981                         feedback
11982                     ] 
11983                 };
11984             } else {
11985                 inputblock = {
11986                     cls : 'has-feedback',
11987                     cn :  [
11988                         inputblock,
11989                         feedback
11990                     ] 
11991                 };
11992             }
11993
11994         } else {
11995             if(this.removable && !this.editable ){
11996                 inputblock = {
11997                     cls : 'roo-removable',
11998                     cn :  [
11999                         inputblock,
12000                         {
12001                             tag: 'button',
12002                             html : 'x',
12003                             cls : 'roo-combo-removable-btn close'
12004                         }
12005                     ] 
12006                 };
12007             }
12008         }
12009         
12010         if (this.before || this.after) {
12011             
12012             inputblock = {
12013                 cls : 'input-group',
12014                 cn :  [] 
12015             };
12016             if (this.before) {
12017                 inputblock.cn.push({
12018                     tag :'span',
12019                     cls : 'input-group-addon input-group-prepend input-group-text',
12020                     html : this.before
12021                 });
12022             }
12023             
12024             inputblock.cn.push(input);
12025             
12026             if(this.hasFeedback && !this.allowBlank){
12027                 inputblock.cls += ' has-feedback';
12028                 inputblock.cn.push(feedback);
12029             }
12030             
12031             if (this.after) {
12032                 inputblock.cn.push({
12033                     tag :'span',
12034                     cls : 'input-group-addon input-group-append input-group-text',
12035                     html : this.after
12036                 });
12037             }
12038             
12039         };
12040         
12041       
12042         
12043         var ibwrap = inputblock;
12044         
12045         if(this.multiple){
12046             ibwrap = {
12047                 tag: 'ul',
12048                 cls: 'roo-select2-choices',
12049                 cn:[
12050                     {
12051                         tag: 'li',
12052                         cls: 'roo-select2-search-field',
12053                         cn: [
12054
12055                             inputblock
12056                         ]
12057                     }
12058                 ]
12059             };
12060                 
12061         }
12062         
12063         var combobox = {
12064             cls: 'roo-select2-container input-group',
12065             cn: [
12066                  {
12067                     tag: 'input',
12068                     type : 'hidden',
12069                     cls: 'form-hidden-field'
12070                 },
12071                 ibwrap
12072             ]
12073         };
12074         
12075         if(!this.multiple && this.showToggleBtn){
12076             
12077             var caret = {
12078                         tag: 'span',
12079                         cls: 'caret'
12080              };
12081             if (this.caret != false) {
12082                 caret = {
12083                      tag: 'i',
12084                      cls: 'fa fa-' + this.caret
12085                 };
12086                 
12087             }
12088             
12089             combobox.cn.push({
12090                 tag :'span',
12091                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12092                 cn : [
12093                     Roo.bootstrap.version == 3 ? caret : '',
12094                     {
12095                         tag: 'span',
12096                         cls: 'combobox-clear',
12097                         cn  : [
12098                             {
12099                                 tag : 'i',
12100                                 cls: 'icon-remove'
12101                             }
12102                         ]
12103                     }
12104                 ]
12105
12106             })
12107         }
12108         
12109         if(this.multiple){
12110             combobox.cls += ' roo-select2-container-multi';
12111         }
12112          var indicator = {
12113             tag : 'i',
12114             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12115             tooltip : 'This field is required'
12116         };
12117         if (Roo.bootstrap.version == 4) {
12118             indicator = {
12119                 tag : 'i',
12120                 style : 'display:none'
12121             };
12122         }
12123         
12124         
12125         if (align ==='left' && this.fieldLabel.length) {
12126             
12127             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12128
12129             cfg.cn = [
12130                 indicator,
12131                 {
12132                     tag: 'label',
12133                     'for' :  id,
12134                     cls : 'control-label',
12135                     html : this.fieldLabel
12136
12137                 },
12138                 {
12139                     cls : "", 
12140                     cn: [
12141                         combobox
12142                     ]
12143                 }
12144
12145             ];
12146             
12147             var labelCfg = cfg.cn[1];
12148             var contentCfg = cfg.cn[2];
12149             
12150             if(this.indicatorpos == 'right'){
12151                 cfg.cn = [
12152                     {
12153                         tag: 'label',
12154                         'for' :  id,
12155                         cls : 'control-label',
12156                         cn : [
12157                             {
12158                                 tag : 'span',
12159                                 html : this.fieldLabel
12160                             },
12161                             indicator
12162                         ]
12163                     },
12164                     {
12165                         cls : "", 
12166                         cn: [
12167                             combobox
12168                         ]
12169                     }
12170
12171                 ];
12172                 
12173                 labelCfg = cfg.cn[0];
12174                 contentCfg = cfg.cn[1];
12175             }
12176             
12177             if(this.labelWidth > 12){
12178                 labelCfg.style = "width: " + this.labelWidth + 'px';
12179             }
12180             
12181             if(this.labelWidth < 13 && this.labelmd == 0){
12182                 this.labelmd = this.labelWidth;
12183             }
12184             
12185             if(this.labellg > 0){
12186                 labelCfg.cls += ' col-lg-' + this.labellg;
12187                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12188             }
12189             
12190             if(this.labelmd > 0){
12191                 labelCfg.cls += ' col-md-' + this.labelmd;
12192                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12193             }
12194             
12195             if(this.labelsm > 0){
12196                 labelCfg.cls += ' col-sm-' + this.labelsm;
12197                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12198             }
12199             
12200             if(this.labelxs > 0){
12201                 labelCfg.cls += ' col-xs-' + this.labelxs;
12202                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12203             }
12204             
12205         } else if ( this.fieldLabel.length) {
12206 //                Roo.log(" label");
12207             cfg.cn = [
12208                 indicator,
12209                {
12210                    tag: 'label',
12211                    //cls : 'input-group-addon',
12212                    html : this.fieldLabel
12213
12214                },
12215
12216                combobox
12217
12218             ];
12219             
12220             if(this.indicatorpos == 'right'){
12221                 
12222                 cfg.cn = [
12223                     {
12224                        tag: 'label',
12225                        cn : [
12226                            {
12227                                tag : 'span',
12228                                html : this.fieldLabel
12229                            },
12230                            indicator
12231                        ]
12232
12233                     },
12234                     combobox
12235
12236                 ];
12237
12238             }
12239
12240         } else {
12241             
12242 //                Roo.log(" no label && no align");
12243                 cfg = combobox
12244                      
12245                 
12246         }
12247         
12248         var settings=this;
12249         ['xs','sm','md','lg'].map(function(size){
12250             if (settings[size]) {
12251                 cfg.cls += ' col-' + size + '-' + settings[size];
12252             }
12253         });
12254         
12255         return cfg;
12256         
12257     },
12258     
12259     
12260     
12261     // private
12262     onResize : function(w, h){
12263 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12264 //        if(typeof w == 'number'){
12265 //            var x = w - this.trigger.getWidth();
12266 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12267 //            this.trigger.setStyle('left', x+'px');
12268 //        }
12269     },
12270
12271     // private
12272     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12273
12274     // private
12275     getResizeEl : function(){
12276         return this.inputEl();
12277     },
12278
12279     // private
12280     getPositionEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     alignErrorIcon : function(){
12286         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12287     },
12288
12289     // private
12290     initEvents : function(){
12291         
12292         this.createList();
12293         
12294         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12295         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12296         if(!this.multiple && this.showToggleBtn){
12297             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12298             if(this.hideTrigger){
12299                 this.trigger.setDisplayed(false);
12300             }
12301             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12302         }
12303         
12304         if(this.multiple){
12305             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12306         }
12307         
12308         if(this.removable && !this.editable && !this.tickable){
12309             var close = this.closeTriggerEl();
12310             
12311             if(close){
12312                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12313                 close.on('click', this.removeBtnClick, this, close);
12314             }
12315         }
12316         
12317         //this.trigger.addClassOnOver('x-form-trigger-over');
12318         //this.trigger.addClassOnClick('x-form-trigger-click');
12319         
12320         //if(!this.width){
12321         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12322         //}
12323     },
12324     
12325     closeTriggerEl : function()
12326     {
12327         var close = this.el.select('.roo-combo-removable-btn', true).first();
12328         return close ? close : false;
12329     },
12330     
12331     removeBtnClick : function(e, h, el)
12332     {
12333         e.preventDefault();
12334         
12335         if(this.fireEvent("remove", this) !== false){
12336             this.reset();
12337             this.fireEvent("afterremove", this)
12338         }
12339     },
12340     
12341     createList : function()
12342     {
12343         this.list = Roo.get(document.body).createChild({
12344             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12345             cls: 'typeahead typeahead-long dropdown-menu shadow',
12346             style: 'display:none'
12347         });
12348         
12349         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12350         
12351     },
12352
12353     // private
12354     initTrigger : function(){
12355        
12356     },
12357
12358     // private
12359     onDestroy : function(){
12360         if(this.trigger){
12361             this.trigger.removeAllListeners();
12362           //  this.trigger.remove();
12363         }
12364         //if(this.wrap){
12365         //    this.wrap.remove();
12366         //}
12367         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12368     },
12369
12370     // private
12371     onFocus : function(){
12372         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12373         /*
12374         if(!this.mimicing){
12375             this.wrap.addClass('x-trigger-wrap-focus');
12376             this.mimicing = true;
12377             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12378             if(this.monitorTab){
12379                 this.el.on("keydown", this.checkTab, this);
12380             }
12381         }
12382         */
12383     },
12384
12385     // private
12386     checkTab : function(e){
12387         if(e.getKey() == e.TAB){
12388             this.triggerBlur();
12389         }
12390     },
12391
12392     // private
12393     onBlur : function(){
12394         // do nothing
12395     },
12396
12397     // private
12398     mimicBlur : function(e, t){
12399         /*
12400         if(!this.wrap.contains(t) && this.validateBlur()){
12401             this.triggerBlur();
12402         }
12403         */
12404     },
12405
12406     // private
12407     triggerBlur : function(){
12408         this.mimicing = false;
12409         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12410         if(this.monitorTab){
12411             this.el.un("keydown", this.checkTab, this);
12412         }
12413         //this.wrap.removeClass('x-trigger-wrap-focus');
12414         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12415     },
12416
12417     // private
12418     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12419     validateBlur : function(e, t){
12420         return true;
12421     },
12422
12423     // private
12424     onDisable : function(){
12425         this.inputEl().dom.disabled = true;
12426         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12427         //if(this.wrap){
12428         //    this.wrap.addClass('x-item-disabled');
12429         //}
12430     },
12431
12432     // private
12433     onEnable : function(){
12434         this.inputEl().dom.disabled = false;
12435         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12436         //if(this.wrap){
12437         //    this.el.removeClass('x-item-disabled');
12438         //}
12439     },
12440
12441     // private
12442     onShow : function(){
12443         var ae = this.getActionEl();
12444         
12445         if(ae){
12446             ae.dom.style.display = '';
12447             ae.dom.style.visibility = 'visible';
12448         }
12449     },
12450
12451     // private
12452     
12453     onHide : function(){
12454         var ae = this.getActionEl();
12455         ae.dom.style.display = 'none';
12456     },
12457
12458     /**
12459      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12460      * by an implementing function.
12461      * @method
12462      * @param {EventObject} e
12463      */
12464     onTriggerClick : Roo.emptyFn
12465 });
12466  
12467 /*
12468 * Licence: LGPL
12469 */
12470
12471 /**
12472  * @class Roo.bootstrap.CardUploader
12473  * @extends Roo.bootstrap.Button
12474  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12475  * @cfg {Number} errorTimeout default 3000
12476  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12477  * @cfg {Array}  html The button text.
12478
12479  *
12480  * @constructor
12481  * Create a new CardUploader
12482  * @param {Object} config The config object
12483  */
12484
12485 Roo.bootstrap.CardUploader = function(config){
12486     
12487  
12488     
12489     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12490     
12491     
12492     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12493         return r.data.id
12494         });
12495     
12496     
12497 };
12498
12499 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12500     
12501      
12502     errorTimeout : 3000,
12503      
12504     images : false,
12505    
12506     fileCollection : false,
12507     allowBlank : true,
12508     
12509     getAutoCreate : function()
12510     {
12511         
12512         var cfg =  {
12513             cls :'form-group' ,
12514             cn : [
12515                
12516                 {
12517                     tag: 'label',
12518                    //cls : 'input-group-addon',
12519                     html : this.fieldLabel
12520
12521                 },
12522
12523                 {
12524                     tag: 'input',
12525                     type : 'hidden',
12526                     value : this.value,
12527                     cls : 'd-none  form-control'
12528                 },
12529                 
12530                 {
12531                     tag: 'input',
12532                     multiple : 'multiple',
12533                     type : 'file',
12534                     cls : 'd-none  roo-card-upload-selector'
12535                 },
12536                 
12537                 {
12538                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12539                 },
12540                 {
12541                     cls : 'card-columns roo-card-uploader-container'
12542                 }
12543
12544             ]
12545         };
12546            
12547          
12548         return cfg;
12549     },
12550     
12551     getChildContainer : function() /// what children are added to.
12552     {
12553         return this.containerEl;
12554     },
12555    
12556     getButtonContainer : function() /// what children are added to.
12557     {
12558         return this.el.select(".roo-card-uploader-button-container").first();
12559     },
12560    
12561     initEvents : function()
12562     {
12563         
12564         Roo.bootstrap.Input.prototype.initEvents.call(this);
12565         
12566         var t = this;
12567         this.addxtype({
12568             xns: Roo.bootstrap,
12569
12570             xtype : 'Button',
12571             container_method : 'getButtonContainer' ,            
12572             html :  this.html, // fix changable?
12573             cls : 'w-100 ',
12574             listeners : {
12575                 'click' : function(btn, e) {
12576                     t.onClick(e);
12577                 }
12578             }
12579         });
12580         
12581         
12582         
12583         
12584         this.urlAPI = (window.createObjectURL && window) || 
12585                                 (window.URL && URL.revokeObjectURL && URL) || 
12586                                 (window.webkitURL && webkitURL);
12587                         
12588          
12589          
12590          
12591         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12592         
12593         this.selectorEl.on('change', this.onFileSelected, this);
12594         if (this.images) {
12595             var t = this;
12596             this.images.forEach(function(img) {
12597                 t.addCard(img)
12598             });
12599             this.images = false;
12600         }
12601         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12602          
12603        
12604     },
12605     
12606    
12607     onClick : function(e)
12608     {
12609         e.preventDefault();
12610          
12611         this.selectorEl.dom.click();
12612          
12613     },
12614     
12615     onFileSelected : function(e)
12616     {
12617         e.preventDefault();
12618         
12619         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12620             return;
12621         }
12622         
12623         Roo.each(this.selectorEl.dom.files, function(file){    
12624             this.addFile(file);
12625         }, this);
12626          
12627     },
12628     
12629       
12630     
12631       
12632     
12633     addFile : function(file)
12634     {
12635            
12636         if(typeof(file) === 'string'){
12637             throw "Add file by name?"; // should not happen
12638             return;
12639         }
12640         
12641         if(!file || !this.urlAPI){
12642             return;
12643         }
12644         
12645         // file;
12646         // file.type;
12647         
12648         var _this = this;
12649         
12650         
12651         var url = _this.urlAPI.createObjectURL( file);
12652            
12653         this.addCard({
12654             id : Roo.bootstrap.CardUploader.ID--,
12655             is_uploaded : false,
12656             src : url,
12657             title : file.name,
12658             mimetype : file.type,
12659             preview : false,
12660             is_deleted : 0
12661         })
12662         
12663     },
12664     
12665     addCard : function (data)
12666     {
12667         // hidden input element?
12668         // if the file is not an image...
12669         //then we need to use something other that and header_image
12670         var t = this;
12671         //   remove.....
12672         var footer = [
12673             {
12674                 xns : Roo.bootstrap,
12675                 xtype : 'CardFooter',
12676                 items: [
12677                     {
12678                         xns : Roo.bootstrap,
12679                         xtype : 'Element',
12680                         cls : 'd-flex',
12681                         items : [
12682                             
12683                             {
12684                                 xns : Roo.bootstrap,
12685                                 xtype : 'Button',
12686                                 html : String.format("<small>{0}</small>", data.title),
12687                                 cls : 'col-11 text-left',
12688                                 size: 'sm',
12689                                 weight: 'link',
12690                                 fa : 'download',
12691                                 listeners : {
12692                                     click : function() {
12693                                         this.downloadCard(data.id)
12694                                     }
12695                                 }
12696                             },
12697                           
12698                             {
12699                                 xns : Roo.bootstrap,
12700                                 xtype : 'Button',
12701                                 
12702                                 size : 'sm',
12703                                 weight: 'danger',
12704                                 cls : 'col-1',
12705                                 fa : 'times',
12706                                 listeners : {
12707                                     click : function() {
12708                                         t.removeCard(data.id)
12709                                     }
12710                                 }
12711                             }
12712                         ]
12713                     }
12714                     
12715                 ] 
12716             }
12717             
12718         ];
12719
12720         var cn = this.addxtype(
12721             {
12722                  
12723                 xns : Roo.bootstrap,
12724                 xtype : 'Card',
12725                 closeable : true,
12726                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12727                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12728                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12729                 data : data,
12730                 html : false,
12731                  
12732                 items : footer,
12733                 initEvents : function() {
12734                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12735                     this.imgEl = this.el.select('.card-img-top').first();
12736                     if (this.imgEl) {
12737                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12738                         this.imgEl.set({ 'pointer' : 'cursor' });
12739                                   
12740                     }
12741                     
12742                   
12743                 }
12744                 
12745             }
12746         );
12747         // dont' really need ot update items.
12748         // this.items.push(cn);
12749         this.fileCollection.add(cn);
12750         this.updateInput();
12751         
12752     },
12753     removeCard : function(id)
12754     {
12755         
12756         var card  = this.fileCollection.get(id);
12757         card.data.is_deleted = 1;
12758         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12759         this.fileCollection.remove(card);
12760         //this.items = this.items.filter(function(e) { return e != card });
12761         // dont' really need ot update items.
12762         card.el.dom.parentNode.removeChild(card.el.dom);
12763         
12764     },
12765     reset: function()
12766     {
12767         this.fileCollection.each(function(card) {
12768             card.el.dom.parentNode.removeChild(card.el.dom);    
12769         });
12770         this.fileCollection.clear();
12771         this.updateInput();
12772     },
12773     
12774     updateInput : function()
12775     {
12776         var data = [];
12777         this.fileCollection.each(function(e) {
12778             data.push(e.data);
12779         });
12780         
12781         this.inputEl().dom.value = JSON.stringify(data);
12782     }
12783     
12784     
12785 });
12786
12787
12788 Roo.bootstrap.CardUploader.ID = -1;/*
12789  * Based on:
12790  * Ext JS Library 1.1.1
12791  * Copyright(c) 2006-2007, Ext JS, LLC.
12792  *
12793  * Originally Released Under LGPL - original licence link has changed is not relivant.
12794  *
12795  * Fork - LGPL
12796  * <script type="text/javascript">
12797  */
12798
12799
12800 /**
12801  * @class Roo.data.SortTypes
12802  * @singleton
12803  * Defines the default sorting (casting?) comparison functions used when sorting data.
12804  */
12805 Roo.data.SortTypes = {
12806     /**
12807      * Default sort that does nothing
12808      * @param {Mixed} s The value being converted
12809      * @return {Mixed} The comparison value
12810      */
12811     none : function(s){
12812         return s;
12813     },
12814     
12815     /**
12816      * The regular expression used to strip tags
12817      * @type {RegExp}
12818      * @property
12819      */
12820     stripTagsRE : /<\/?[^>]+>/gi,
12821     
12822     /**
12823      * Strips all HTML tags to sort on text only
12824      * @param {Mixed} s The value being converted
12825      * @return {String} The comparison value
12826      */
12827     asText : function(s){
12828         return String(s).replace(this.stripTagsRE, "");
12829     },
12830     
12831     /**
12832      * Strips all HTML tags to sort on text only - Case insensitive
12833      * @param {Mixed} s The value being converted
12834      * @return {String} The comparison value
12835      */
12836     asUCText : function(s){
12837         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12838     },
12839     
12840     /**
12841      * Case insensitive string
12842      * @param {Mixed} s The value being converted
12843      * @return {String} The comparison value
12844      */
12845     asUCString : function(s) {
12846         return String(s).toUpperCase();
12847     },
12848     
12849     /**
12850      * Date sorting
12851      * @param {Mixed} s The value being converted
12852      * @return {Number} The comparison value
12853      */
12854     asDate : function(s) {
12855         if(!s){
12856             return 0;
12857         }
12858         if(s instanceof Date){
12859             return s.getTime();
12860         }
12861         return Date.parse(String(s));
12862     },
12863     
12864     /**
12865      * Float sorting
12866      * @param {Mixed} s The value being converted
12867      * @return {Float} The comparison value
12868      */
12869     asFloat : function(s) {
12870         var val = parseFloat(String(s).replace(/,/g, ""));
12871         if(isNaN(val)) {
12872             val = 0;
12873         }
12874         return val;
12875     },
12876     
12877     /**
12878      * Integer sorting
12879      * @param {Mixed} s The value being converted
12880      * @return {Number} The comparison value
12881      */
12882     asInt : function(s) {
12883         var val = parseInt(String(s).replace(/,/g, ""));
12884         if(isNaN(val)) {
12885             val = 0;
12886         }
12887         return val;
12888     }
12889 };/*
12890  * Based on:
12891  * Ext JS Library 1.1.1
12892  * Copyright(c) 2006-2007, Ext JS, LLC.
12893  *
12894  * Originally Released Under LGPL - original licence link has changed is not relivant.
12895  *
12896  * Fork - LGPL
12897  * <script type="text/javascript">
12898  */
12899
12900 /**
12901 * @class Roo.data.Record
12902  * Instances of this class encapsulate both record <em>definition</em> information, and record
12903  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12904  * to access Records cached in an {@link Roo.data.Store} object.<br>
12905  * <p>
12906  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12907  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12908  * objects.<br>
12909  * <p>
12910  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12911  * @constructor
12912  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12913  * {@link #create}. The parameters are the same.
12914  * @param {Array} data An associative Array of data values keyed by the field name.
12915  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12916  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12917  * not specified an integer id is generated.
12918  */
12919 Roo.data.Record = function(data, id){
12920     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12921     this.data = data;
12922 };
12923
12924 /**
12925  * Generate a constructor for a specific record layout.
12926  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12927  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12928  * Each field definition object may contain the following properties: <ul>
12929  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
12930  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12931  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12932  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12933  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12934  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12935  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12936  * this may be omitted.</p></li>
12937  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12938  * <ul><li>auto (Default, implies no conversion)</li>
12939  * <li>string</li>
12940  * <li>int</li>
12941  * <li>float</li>
12942  * <li>boolean</li>
12943  * <li>date</li></ul></p></li>
12944  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12945  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12946  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12947  * by the Reader into an object that will be stored in the Record. It is passed the
12948  * following parameters:<ul>
12949  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12950  * </ul></p></li>
12951  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12952  * </ul>
12953  * <br>usage:<br><pre><code>
12954 var TopicRecord = Roo.data.Record.create(
12955     {name: 'title', mapping: 'topic_title'},
12956     {name: 'author', mapping: 'username'},
12957     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12958     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12959     {name: 'lastPoster', mapping: 'user2'},
12960     {name: 'excerpt', mapping: 'post_text'}
12961 );
12962
12963 var myNewRecord = new TopicRecord({
12964     title: 'Do my job please',
12965     author: 'noobie',
12966     totalPosts: 1,
12967     lastPost: new Date(),
12968     lastPoster: 'Animal',
12969     excerpt: 'No way dude!'
12970 });
12971 myStore.add(myNewRecord);
12972 </code></pre>
12973  * @method create
12974  * @static
12975  */
12976 Roo.data.Record.create = function(o){
12977     var f = function(){
12978         f.superclass.constructor.apply(this, arguments);
12979     };
12980     Roo.extend(f, Roo.data.Record);
12981     var p = f.prototype;
12982     p.fields = new Roo.util.MixedCollection(false, function(field){
12983         return field.name;
12984     });
12985     for(var i = 0, len = o.length; i < len; i++){
12986         p.fields.add(new Roo.data.Field(o[i]));
12987     }
12988     f.getField = function(name){
12989         return p.fields.get(name);  
12990     };
12991     return f;
12992 };
12993
12994 Roo.data.Record.AUTO_ID = 1000;
12995 Roo.data.Record.EDIT = 'edit';
12996 Roo.data.Record.REJECT = 'reject';
12997 Roo.data.Record.COMMIT = 'commit';
12998
12999 Roo.data.Record.prototype = {
13000     /**
13001      * Readonly flag - true if this record has been modified.
13002      * @type Boolean
13003      */
13004     dirty : false,
13005     editing : false,
13006     error: null,
13007     modified: null,
13008
13009     // private
13010     join : function(store){
13011         this.store = store;
13012     },
13013
13014     /**
13015      * Set the named field to the specified value.
13016      * @param {String} name The name of the field to set.
13017      * @param {Object} value The value to set the field to.
13018      */
13019     set : function(name, value){
13020         if(this.data[name] == value){
13021             return;
13022         }
13023         this.dirty = true;
13024         if(!this.modified){
13025             this.modified = {};
13026         }
13027         if(typeof this.modified[name] == 'undefined'){
13028             this.modified[name] = this.data[name];
13029         }
13030         this.data[name] = value;
13031         if(!this.editing && this.store){
13032             this.store.afterEdit(this);
13033         }       
13034     },
13035
13036     /**
13037      * Get the value of the named field.
13038      * @param {String} name The name of the field to get the value of.
13039      * @return {Object} The value of the field.
13040      */
13041     get : function(name){
13042         return this.data[name]; 
13043     },
13044
13045     // private
13046     beginEdit : function(){
13047         this.editing = true;
13048         this.modified = {}; 
13049     },
13050
13051     // private
13052     cancelEdit : function(){
13053         this.editing = false;
13054         delete this.modified;
13055     },
13056
13057     // private
13058     endEdit : function(){
13059         this.editing = false;
13060         if(this.dirty && this.store){
13061             this.store.afterEdit(this);
13062         }
13063     },
13064
13065     /**
13066      * Usually called by the {@link Roo.data.Store} which owns the Record.
13067      * Rejects all changes made to the Record since either creation, or the last commit operation.
13068      * Modified fields are reverted to their original values.
13069      * <p>
13070      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13071      * of reject operations.
13072      */
13073     reject : function(){
13074         var m = this.modified;
13075         for(var n in m){
13076             if(typeof m[n] != "function"){
13077                 this.data[n] = m[n];
13078             }
13079         }
13080         this.dirty = false;
13081         delete this.modified;
13082         this.editing = false;
13083         if(this.store){
13084             this.store.afterReject(this);
13085         }
13086     },
13087
13088     /**
13089      * Usually called by the {@link Roo.data.Store} which owns the Record.
13090      * Commits all changes made to the Record since either creation, or the last commit operation.
13091      * <p>
13092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13093      * of commit operations.
13094      */
13095     commit : function(){
13096         this.dirty = false;
13097         delete this.modified;
13098         this.editing = false;
13099         if(this.store){
13100             this.store.afterCommit(this);
13101         }
13102     },
13103
13104     // private
13105     hasError : function(){
13106         return this.error != null;
13107     },
13108
13109     // private
13110     clearError : function(){
13111         this.error = null;
13112     },
13113
13114     /**
13115      * Creates a copy of this record.
13116      * @param {String} id (optional) A new record id if you don't want to use this record's id
13117      * @return {Record}
13118      */
13119     copy : function(newId) {
13120         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13121     }
13122 };/*
13123  * Based on:
13124  * Ext JS Library 1.1.1
13125  * Copyright(c) 2006-2007, Ext JS, LLC.
13126  *
13127  * Originally Released Under LGPL - original licence link has changed is not relivant.
13128  *
13129  * Fork - LGPL
13130  * <script type="text/javascript">
13131  */
13132
13133
13134
13135 /**
13136  * @class Roo.data.Store
13137  * @extends Roo.util.Observable
13138  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13139  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13140  * <p>
13141  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
13142  * has no knowledge of the format of the data returned by the Proxy.<br>
13143  * <p>
13144  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13145  * instances from the data object. These records are cached and made available through accessor functions.
13146  * @constructor
13147  * Creates a new Store.
13148  * @param {Object} config A config object containing the objects needed for the Store to access data,
13149  * and read the data into Records.
13150  */
13151 Roo.data.Store = function(config){
13152     this.data = new Roo.util.MixedCollection(false);
13153     this.data.getKey = function(o){
13154         return o.id;
13155     };
13156     this.baseParams = {};
13157     // private
13158     this.paramNames = {
13159         "start" : "start",
13160         "limit" : "limit",
13161         "sort" : "sort",
13162         "dir" : "dir",
13163         "multisort" : "_multisort"
13164     };
13165
13166     if(config && config.data){
13167         this.inlineData = config.data;
13168         delete config.data;
13169     }
13170
13171     Roo.apply(this, config);
13172     
13173     if(this.reader){ // reader passed
13174         this.reader = Roo.factory(this.reader, Roo.data);
13175         this.reader.xmodule = this.xmodule || false;
13176         if(!this.recordType){
13177             this.recordType = this.reader.recordType;
13178         }
13179         if(this.reader.onMetaChange){
13180             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13181         }
13182     }
13183
13184     if(this.recordType){
13185         this.fields = this.recordType.prototype.fields;
13186     }
13187     this.modified = [];
13188
13189     this.addEvents({
13190         /**
13191          * @event datachanged
13192          * Fires when the data cache has changed, and a widget which is using this Store
13193          * as a Record cache should refresh its view.
13194          * @param {Store} this
13195          */
13196         datachanged : true,
13197         /**
13198          * @event metachange
13199          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13200          * @param {Store} this
13201          * @param {Object} meta The JSON metadata
13202          */
13203         metachange : true,
13204         /**
13205          * @event add
13206          * Fires when Records have been added to the Store
13207          * @param {Store} this
13208          * @param {Roo.data.Record[]} records The array of Records added
13209          * @param {Number} index The index at which the record(s) were added
13210          */
13211         add : true,
13212         /**
13213          * @event remove
13214          * Fires when a Record has been removed from the Store
13215          * @param {Store} this
13216          * @param {Roo.data.Record} record The Record that was removed
13217          * @param {Number} index The index at which the record was removed
13218          */
13219         remove : true,
13220         /**
13221          * @event update
13222          * Fires when a Record has been updated
13223          * @param {Store} this
13224          * @param {Roo.data.Record} record The Record that was updated
13225          * @param {String} operation The update operation being performed.  Value may be one of:
13226          * <pre><code>
13227  Roo.data.Record.EDIT
13228  Roo.data.Record.REJECT
13229  Roo.data.Record.COMMIT
13230          * </code></pre>
13231          */
13232         update : true,
13233         /**
13234          * @event clear
13235          * Fires when the data cache has been cleared.
13236          * @param {Store} this
13237          */
13238         clear : true,
13239         /**
13240          * @event beforeload
13241          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13242          * the load action will be canceled.
13243          * @param {Store} this
13244          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13245          */
13246         beforeload : true,
13247         /**
13248          * @event beforeloadadd
13249          * Fires after a new set of Records has been loaded.
13250          * @param {Store} this
13251          * @param {Roo.data.Record[]} records The Records that were loaded
13252          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13253          */
13254         beforeloadadd : true,
13255         /**
13256          * @event load
13257          * Fires after a new set of Records has been loaded, before they are added to the store.
13258          * @param {Store} this
13259          * @param {Roo.data.Record[]} records The Records that were loaded
13260          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13261          * @params {Object} return from reader
13262          */
13263         load : true,
13264         /**
13265          * @event loadexception
13266          * Fires if an exception occurs in the Proxy during loading.
13267          * Called with the signature of the Proxy's "loadexception" event.
13268          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13269          * 
13270          * @param {Proxy} 
13271          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13272          * @param {Object} load options 
13273          * @param {Object} jsonData from your request (normally this contains the Exception)
13274          */
13275         loadexception : true
13276     });
13277     
13278     if(this.proxy){
13279         this.proxy = Roo.factory(this.proxy, Roo.data);
13280         this.proxy.xmodule = this.xmodule || false;
13281         this.relayEvents(this.proxy,  ["loadexception"]);
13282     }
13283     this.sortToggle = {};
13284     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13285
13286     Roo.data.Store.superclass.constructor.call(this);
13287
13288     if(this.inlineData){
13289         this.loadData(this.inlineData);
13290         delete this.inlineData;
13291     }
13292 };
13293
13294 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13295      /**
13296     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13297     * without a remote query - used by combo/forms at present.
13298     */
13299     
13300     /**
13301     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13302     */
13303     /**
13304     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13305     */
13306     /**
13307     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13308     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13309     */
13310     /**
13311     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13312     * on any HTTP request
13313     */
13314     /**
13315     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13316     */
13317     /**
13318     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13319     */
13320     multiSort: false,
13321     /**
13322     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13323     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13324     */
13325     remoteSort : false,
13326
13327     /**
13328     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13329      * loaded or when a record is removed. (defaults to false).
13330     */
13331     pruneModifiedRecords : false,
13332
13333     // private
13334     lastOptions : null,
13335
13336     /**
13337      * Add Records to the Store and fires the add event.
13338      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13339      */
13340     add : function(records){
13341         records = [].concat(records);
13342         for(var i = 0, len = records.length; i < len; i++){
13343             records[i].join(this);
13344         }
13345         var index = this.data.length;
13346         this.data.addAll(records);
13347         this.fireEvent("add", this, records, index);
13348     },
13349
13350     /**
13351      * Remove a Record from the Store and fires the remove event.
13352      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13353      */
13354     remove : function(record){
13355         var index = this.data.indexOf(record);
13356         this.data.removeAt(index);
13357  
13358         if(this.pruneModifiedRecords){
13359             this.modified.remove(record);
13360         }
13361         this.fireEvent("remove", this, record, index);
13362     },
13363
13364     /**
13365      * Remove all Records from the Store and fires the clear event.
13366      */
13367     removeAll : function(){
13368         this.data.clear();
13369         if(this.pruneModifiedRecords){
13370             this.modified = [];
13371         }
13372         this.fireEvent("clear", this);
13373     },
13374
13375     /**
13376      * Inserts Records to the Store at the given index and fires the add event.
13377      * @param {Number} index The start index at which to insert the passed Records.
13378      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13379      */
13380     insert : function(index, records){
13381         records = [].concat(records);
13382         for(var i = 0, len = records.length; i < len; i++){
13383             this.data.insert(index, records[i]);
13384             records[i].join(this);
13385         }
13386         this.fireEvent("add", this, records, index);
13387     },
13388
13389     /**
13390      * Get the index within the cache of the passed Record.
13391      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13392      * @return {Number} The index of the passed Record. Returns -1 if not found.
13393      */
13394     indexOf : function(record){
13395         return this.data.indexOf(record);
13396     },
13397
13398     /**
13399      * Get the index within the cache of the Record with the passed id.
13400      * @param {String} id The id of the Record to find.
13401      * @return {Number} The index of the Record. Returns -1 if not found.
13402      */
13403     indexOfId : function(id){
13404         return this.data.indexOfKey(id);
13405     },
13406
13407     /**
13408      * Get the Record with the specified id.
13409      * @param {String} id The id of the Record to find.
13410      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13411      */
13412     getById : function(id){
13413         return this.data.key(id);
13414     },
13415
13416     /**
13417      * Get the Record at the specified index.
13418      * @param {Number} index The index of the Record to find.
13419      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13420      */
13421     getAt : function(index){
13422         return this.data.itemAt(index);
13423     },
13424
13425     /**
13426      * Returns a range of Records between specified indices.
13427      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13428      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13429      * @return {Roo.data.Record[]} An array of Records
13430      */
13431     getRange : function(start, end){
13432         return this.data.getRange(start, end);
13433     },
13434
13435     // private
13436     storeOptions : function(o){
13437         o = Roo.apply({}, o);
13438         delete o.callback;
13439         delete o.scope;
13440         this.lastOptions = o;
13441     },
13442
13443     /**
13444      * Loads the Record cache from the configured Proxy using the configured Reader.
13445      * <p>
13446      * If using remote paging, then the first load call must specify the <em>start</em>
13447      * and <em>limit</em> properties in the options.params property to establish the initial
13448      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13449      * <p>
13450      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13451      * and this call will return before the new data has been loaded. Perform any post-processing
13452      * in a callback function, or in a "load" event handler.</strong>
13453      * <p>
13454      * @param {Object} options An object containing properties which control loading options:<ul>
13455      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13456      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13457      * passed the following arguments:<ul>
13458      * <li>r : Roo.data.Record[]</li>
13459      * <li>options: Options object from the load call</li>
13460      * <li>success: Boolean success indicator</li></ul></li>
13461      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13462      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13463      * </ul>
13464      */
13465     load : function(options){
13466         options = options || {};
13467         if(this.fireEvent("beforeload", this, options) !== false){
13468             this.storeOptions(options);
13469             var p = Roo.apply(options.params || {}, this.baseParams);
13470             // if meta was not loaded from remote source.. try requesting it.
13471             if (!this.reader.metaFromRemote) {
13472                 p._requestMeta = 1;
13473             }
13474             if(this.sortInfo && this.remoteSort){
13475                 var pn = this.paramNames;
13476                 p[pn["sort"]] = this.sortInfo.field;
13477                 p[pn["dir"]] = this.sortInfo.direction;
13478             }
13479             if (this.multiSort) {
13480                 var pn = this.paramNames;
13481                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13482             }
13483             
13484             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13485         }
13486     },
13487
13488     /**
13489      * Reloads the Record cache from the configured Proxy using the configured Reader and
13490      * the options from the last load operation performed.
13491      * @param {Object} options (optional) An object containing properties which may override the options
13492      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13493      * the most recently used options are reused).
13494      */
13495     reload : function(options){
13496         this.load(Roo.applyIf(options||{}, this.lastOptions));
13497     },
13498
13499     // private
13500     // Called as a callback by the Reader during a load operation.
13501     loadRecords : function(o, options, success){
13502         if(!o || success === false){
13503             if(success !== false){
13504                 this.fireEvent("load", this, [], options, o);
13505             }
13506             if(options.callback){
13507                 options.callback.call(options.scope || this, [], options, false);
13508             }
13509             return;
13510         }
13511         // if data returned failure - throw an exception.
13512         if (o.success === false) {
13513             // show a message if no listener is registered.
13514             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13515                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13516             }
13517             // loadmask wil be hooked into this..
13518             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13519             return;
13520         }
13521         var r = o.records, t = o.totalRecords || r.length;
13522         
13523         this.fireEvent("beforeloadadd", this, r, options, o);
13524         
13525         if(!options || options.add !== true){
13526             if(this.pruneModifiedRecords){
13527                 this.modified = [];
13528             }
13529             for(var i = 0, len = r.length; i < len; i++){
13530                 r[i].join(this);
13531             }
13532             if(this.snapshot){
13533                 this.data = this.snapshot;
13534                 delete this.snapshot;
13535             }
13536             this.data.clear();
13537             this.data.addAll(r);
13538             this.totalLength = t;
13539             this.applySort();
13540             this.fireEvent("datachanged", this);
13541         }else{
13542             this.totalLength = Math.max(t, this.data.length+r.length);
13543             this.add(r);
13544         }
13545         
13546         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13547                 
13548             var e = new Roo.data.Record({});
13549
13550             e.set(this.parent.displayField, this.parent.emptyTitle);
13551             e.set(this.parent.valueField, '');
13552
13553             this.insert(0, e);
13554         }
13555             
13556         this.fireEvent("load", this, r, options, o);
13557         if(options.callback){
13558             options.callback.call(options.scope || this, r, options, true);
13559         }
13560     },
13561
13562
13563     /**
13564      * Loads data from a passed data block. A Reader which understands the format of the data
13565      * must have been configured in the constructor.
13566      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13567      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13568      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13569      */
13570     loadData : function(o, append){
13571         var r = this.reader.readRecords(o);
13572         this.loadRecords(r, {add: append}, true);
13573     },
13574     
13575      /**
13576      * using 'cn' the nested child reader read the child array into it's child stores.
13577      * @param {Object} rec The record with a 'children array
13578      */
13579     loadDataFromChildren : function(rec)
13580     {
13581         this.loadData(this.reader.toLoadData(rec));
13582     },
13583     
13584
13585     /**
13586      * Gets the number of cached records.
13587      * <p>
13588      * <em>If using paging, this may not be the total size of the dataset. If the data object
13589      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13590      * the data set size</em>
13591      */
13592     getCount : function(){
13593         return this.data.length || 0;
13594     },
13595
13596     /**
13597      * Gets the total number of records in the dataset as returned by the server.
13598      * <p>
13599      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13600      * the dataset size</em>
13601      */
13602     getTotalCount : function(){
13603         return this.totalLength || 0;
13604     },
13605
13606     /**
13607      * Returns the sort state of the Store as an object with two properties:
13608      * <pre><code>
13609  field {String} The name of the field by which the Records are sorted
13610  direction {String} The sort order, "ASC" or "DESC"
13611      * </code></pre>
13612      */
13613     getSortState : function(){
13614         return this.sortInfo;
13615     },
13616
13617     // private
13618     applySort : function(){
13619         if(this.sortInfo && !this.remoteSort){
13620             var s = this.sortInfo, f = s.field;
13621             var st = this.fields.get(f).sortType;
13622             var fn = function(r1, r2){
13623                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13624                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13625             };
13626             this.data.sort(s.direction, fn);
13627             if(this.snapshot && this.snapshot != this.data){
13628                 this.snapshot.sort(s.direction, fn);
13629             }
13630         }
13631     },
13632
13633     /**
13634      * Sets the default sort column and order to be used by the next load operation.
13635      * @param {String} fieldName The name of the field to sort by.
13636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13637      */
13638     setDefaultSort : function(field, dir){
13639         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13640     },
13641
13642     /**
13643      * Sort the Records.
13644      * If remote sorting is used, the sort is performed on the server, and the cache is
13645      * reloaded. If local sorting is used, the cache is sorted internally.
13646      * @param {String} fieldName The name of the field to sort by.
13647      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13648      */
13649     sort : function(fieldName, dir){
13650         var f = this.fields.get(fieldName);
13651         if(!dir){
13652             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13653             
13654             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13655                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13656             }else{
13657                 dir = f.sortDir;
13658             }
13659         }
13660         this.sortToggle[f.name] = dir;
13661         this.sortInfo = {field: f.name, direction: dir};
13662         if(!this.remoteSort){
13663             this.applySort();
13664             this.fireEvent("datachanged", this);
13665         }else{
13666             this.load(this.lastOptions);
13667         }
13668     },
13669
13670     /**
13671      * Calls the specified function for each of the Records in the cache.
13672      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13673      * Returning <em>false</em> aborts and exits the iteration.
13674      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13675      */
13676     each : function(fn, scope){
13677         this.data.each(fn, scope);
13678     },
13679
13680     /**
13681      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13682      * (e.g., during paging).
13683      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13684      */
13685     getModifiedRecords : function(){
13686         return this.modified;
13687     },
13688
13689     // private
13690     createFilterFn : function(property, value, anyMatch){
13691         if(!value.exec){ // not a regex
13692             value = String(value);
13693             if(value.length == 0){
13694                 return false;
13695             }
13696             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13697         }
13698         return function(r){
13699             return value.test(r.data[property]);
13700         };
13701     },
13702
13703     /**
13704      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13705      * @param {String} property A field on your records
13706      * @param {Number} start The record index to start at (defaults to 0)
13707      * @param {Number} end The last record index to include (defaults to length - 1)
13708      * @return {Number} The sum
13709      */
13710     sum : function(property, start, end){
13711         var rs = this.data.items, v = 0;
13712         start = start || 0;
13713         end = (end || end === 0) ? end : rs.length-1;
13714
13715         for(var i = start; i <= end; i++){
13716             v += (rs[i].data[property] || 0);
13717         }
13718         return v;
13719     },
13720
13721     /**
13722      * Filter the records by a specified property.
13723      * @param {String} field A field on your records
13724      * @param {String/RegExp} value Either a string that the field
13725      * should start with or a RegExp to test against the field
13726      * @param {Boolean} anyMatch True to match any part not just the beginning
13727      */
13728     filter : function(property, value, anyMatch){
13729         var fn = this.createFilterFn(property, value, anyMatch);
13730         return fn ? this.filterBy(fn) : this.clearFilter();
13731     },
13732
13733     /**
13734      * Filter by a function. The specified function will be called with each
13735      * record in this data source. If the function returns true the record is included,
13736      * otherwise it is filtered.
13737      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13738      * @param {Object} scope (optional) The scope of the function (defaults to this)
13739      */
13740     filterBy : function(fn, scope){
13741         this.snapshot = this.snapshot || this.data;
13742         this.data = this.queryBy(fn, scope||this);
13743         this.fireEvent("datachanged", this);
13744     },
13745
13746     /**
13747      * Query the records by a specified property.
13748      * @param {String} field A field on your records
13749      * @param {String/RegExp} value Either a string that the field
13750      * should start with or a RegExp to test against the field
13751      * @param {Boolean} anyMatch True to match any part not just the beginning
13752      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13753      */
13754     query : function(property, value, anyMatch){
13755         var fn = this.createFilterFn(property, value, anyMatch);
13756         return fn ? this.queryBy(fn) : this.data.clone();
13757     },
13758
13759     /**
13760      * Query by a function. The specified function will be called with each
13761      * record in this data source. If the function returns true the record is included
13762      * in the results.
13763      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13764      * @param {Object} scope (optional) The scope of the function (defaults to this)
13765       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13766      **/
13767     queryBy : function(fn, scope){
13768         var data = this.snapshot || this.data;
13769         return data.filterBy(fn, scope||this);
13770     },
13771
13772     /**
13773      * Collects unique values for a particular dataIndex from this store.
13774      * @param {String} dataIndex The property to collect
13775      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13776      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13777      * @return {Array} An array of the unique values
13778      **/
13779     collect : function(dataIndex, allowNull, bypassFilter){
13780         var d = (bypassFilter === true && this.snapshot) ?
13781                 this.snapshot.items : this.data.items;
13782         var v, sv, r = [], l = {};
13783         for(var i = 0, len = d.length; i < len; i++){
13784             v = d[i].data[dataIndex];
13785             sv = String(v);
13786             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13787                 l[sv] = true;
13788                 r[r.length] = v;
13789             }
13790         }
13791         return r;
13792     },
13793
13794     /**
13795      * Revert to a view of the Record cache with no filtering applied.
13796      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13797      */
13798     clearFilter : function(suppressEvent){
13799         if(this.snapshot && this.snapshot != this.data){
13800             this.data = this.snapshot;
13801             delete this.snapshot;
13802             if(suppressEvent !== true){
13803                 this.fireEvent("datachanged", this);
13804             }
13805         }
13806     },
13807
13808     // private
13809     afterEdit : function(record){
13810         if(this.modified.indexOf(record) == -1){
13811             this.modified.push(record);
13812         }
13813         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13814     },
13815     
13816     // private
13817     afterReject : function(record){
13818         this.modified.remove(record);
13819         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13820     },
13821
13822     // private
13823     afterCommit : function(record){
13824         this.modified.remove(record);
13825         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13826     },
13827
13828     /**
13829      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13830      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13831      */
13832     commitChanges : function(){
13833         var m = this.modified.slice(0);
13834         this.modified = [];
13835         for(var i = 0, len = m.length; i < len; i++){
13836             m[i].commit();
13837         }
13838     },
13839
13840     /**
13841      * Cancel outstanding changes on all changed records.
13842      */
13843     rejectChanges : function(){
13844         var m = this.modified.slice(0);
13845         this.modified = [];
13846         for(var i = 0, len = m.length; i < len; i++){
13847             m[i].reject();
13848         }
13849     },
13850
13851     onMetaChange : function(meta, rtype, o){
13852         this.recordType = rtype;
13853         this.fields = rtype.prototype.fields;
13854         delete this.snapshot;
13855         this.sortInfo = meta.sortInfo || this.sortInfo;
13856         this.modified = [];
13857         this.fireEvent('metachange', this, this.reader.meta);
13858     },
13859     
13860     moveIndex : function(data, type)
13861     {
13862         var index = this.indexOf(data);
13863         
13864         var newIndex = index + type;
13865         
13866         this.remove(data);
13867         
13868         this.insert(newIndex, data);
13869         
13870     }
13871 });/*
13872  * Based on:
13873  * Ext JS Library 1.1.1
13874  * Copyright(c) 2006-2007, Ext JS, LLC.
13875  *
13876  * Originally Released Under LGPL - original licence link has changed is not relivant.
13877  *
13878  * Fork - LGPL
13879  * <script type="text/javascript">
13880  */
13881
13882 /**
13883  * @class Roo.data.SimpleStore
13884  * @extends Roo.data.Store
13885  * Small helper class to make creating Stores from Array data easier.
13886  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13887  * @cfg {Array} fields An array of field definition objects, or field name strings.
13888  * @cfg {Object} an existing reader (eg. copied from another store)
13889  * @cfg {Array} data The multi-dimensional array of data
13890  * @constructor
13891  * @param {Object} config
13892  */
13893 Roo.data.SimpleStore = function(config)
13894 {
13895     Roo.data.SimpleStore.superclass.constructor.call(this, {
13896         isLocal : true,
13897         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13898                 id: config.id
13899             },
13900             Roo.data.Record.create(config.fields)
13901         ),
13902         proxy : new Roo.data.MemoryProxy(config.data)
13903     });
13904     this.load();
13905 };
13906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917 /**
13918 /**
13919  * @extends Roo.data.Store
13920  * @class Roo.data.JsonStore
13921  * Small helper class to make creating Stores for JSON data easier. <br/>
13922 <pre><code>
13923 var store = new Roo.data.JsonStore({
13924     url: 'get-images.php',
13925     root: 'images',
13926     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13927 });
13928 </code></pre>
13929  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13930  * JsonReader and HttpProxy (unless inline data is provided).</b>
13931  * @cfg {Array} fields An array of field definition objects, or field name strings.
13932  * @constructor
13933  * @param {Object} config
13934  */
13935 Roo.data.JsonStore = function(c){
13936     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13937         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13938         reader: new Roo.data.JsonReader(c, c.fields)
13939     }));
13940 };
13941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13942  * Based on:
13943  * Ext JS Library 1.1.1
13944  * Copyright(c) 2006-2007, Ext JS, LLC.
13945  *
13946  * Originally Released Under LGPL - original licence link has changed is not relivant.
13947  *
13948  * Fork - LGPL
13949  * <script type="text/javascript">
13950  */
13951
13952  
13953 Roo.data.Field = function(config){
13954     if(typeof config == "string"){
13955         config = {name: config};
13956     }
13957     Roo.apply(this, config);
13958     
13959     if(!this.type){
13960         this.type = "auto";
13961     }
13962     
13963     var st = Roo.data.SortTypes;
13964     // named sortTypes are supported, here we look them up
13965     if(typeof this.sortType == "string"){
13966         this.sortType = st[this.sortType];
13967     }
13968     
13969     // set default sortType for strings and dates
13970     if(!this.sortType){
13971         switch(this.type){
13972             case "string":
13973                 this.sortType = st.asUCString;
13974                 break;
13975             case "date":
13976                 this.sortType = st.asDate;
13977                 break;
13978             default:
13979                 this.sortType = st.none;
13980         }
13981     }
13982
13983     // define once
13984     var stripRe = /[\$,%]/g;
13985
13986     // prebuilt conversion function for this field, instead of
13987     // switching every time we're reading a value
13988     if(!this.convert){
13989         var cv, dateFormat = this.dateFormat;
13990         switch(this.type){
13991             case "":
13992             case "auto":
13993             case undefined:
13994                 cv = function(v){ return v; };
13995                 break;
13996             case "string":
13997                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13998                 break;
13999             case "int":
14000                 cv = function(v){
14001                     return v !== undefined && v !== null && v !== '' ?
14002                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14003                     };
14004                 break;
14005             case "float":
14006                 cv = function(v){
14007                     return v !== undefined && v !== null && v !== '' ?
14008                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14009                     };
14010                 break;
14011             case "bool":
14012             case "boolean":
14013                 cv = function(v){ return v === true || v === "true" || v == 1; };
14014                 break;
14015             case "date":
14016                 cv = function(v){
14017                     if(!v){
14018                         return '';
14019                     }
14020                     if(v instanceof Date){
14021                         return v;
14022                     }
14023                     if(dateFormat){
14024                         if(dateFormat == "timestamp"){
14025                             return new Date(v*1000);
14026                         }
14027                         return Date.parseDate(v, dateFormat);
14028                     }
14029                     var parsed = Date.parse(v);
14030                     return parsed ? new Date(parsed) : null;
14031                 };
14032              break;
14033             
14034         }
14035         this.convert = cv;
14036     }
14037 };
14038
14039 Roo.data.Field.prototype = {
14040     dateFormat: null,
14041     defaultValue: "",
14042     mapping: null,
14043     sortType : null,
14044     sortDir : "ASC"
14045 };/*
14046  * Based on:
14047  * Ext JS Library 1.1.1
14048  * Copyright(c) 2006-2007, Ext JS, LLC.
14049  *
14050  * Originally Released Under LGPL - original licence link has changed is not relivant.
14051  *
14052  * Fork - LGPL
14053  * <script type="text/javascript">
14054  */
14055  
14056 // Base class for reading structured data from a data source.  This class is intended to be
14057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14058
14059 /**
14060  * @class Roo.data.DataReader
14061  * Base class for reading structured data from a data source.  This class is intended to be
14062  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14063  */
14064
14065 Roo.data.DataReader = function(meta, recordType){
14066     
14067     this.meta = meta;
14068     
14069     this.recordType = recordType instanceof Array ? 
14070         Roo.data.Record.create(recordType) : recordType;
14071 };
14072
14073 Roo.data.DataReader.prototype = {
14074     
14075     
14076     readerType : 'Data',
14077      /**
14078      * Create an empty record
14079      * @param {Object} data (optional) - overlay some values
14080      * @return {Roo.data.Record} record created.
14081      */
14082     newRow :  function(d) {
14083         var da =  {};
14084         this.recordType.prototype.fields.each(function(c) {
14085             switch( c.type) {
14086                 case 'int' : da[c.name] = 0; break;
14087                 case 'date' : da[c.name] = new Date(); break;
14088                 case 'float' : da[c.name] = 0.0; break;
14089                 case 'boolean' : da[c.name] = false; break;
14090                 default : da[c.name] = ""; break;
14091             }
14092             
14093         });
14094         return new this.recordType(Roo.apply(da, d));
14095     }
14096     
14097     
14098 };/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108
14109 /**
14110  * @class Roo.data.DataProxy
14111  * @extends Roo.data.Observable
14112  * This class is an abstract base class for implementations which provide retrieval of
14113  * unformatted data objects.<br>
14114  * <p>
14115  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14116  * (of the appropriate type which knows how to parse the data object) to provide a block of
14117  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14118  * <p>
14119  * Custom implementations must implement the load method as described in
14120  * {@link Roo.data.HttpProxy#load}.
14121  */
14122 Roo.data.DataProxy = function(){
14123     this.addEvents({
14124         /**
14125          * @event beforeload
14126          * Fires before a network request is made to retrieve a data object.
14127          * @param {Object} This DataProxy object.
14128          * @param {Object} params The params parameter to the load function.
14129          */
14130         beforeload : true,
14131         /**
14132          * @event load
14133          * Fires before the load method's callback is called.
14134          * @param {Object} This DataProxy object.
14135          * @param {Object} o The data object.
14136          * @param {Object} arg The callback argument object passed to the load function.
14137          */
14138         load : true,
14139         /**
14140          * @event loadexception
14141          * Fires if an Exception occurs during data retrieval.
14142          * @param {Object} This DataProxy object.
14143          * @param {Object} o The data object.
14144          * @param {Object} arg The callback argument object passed to the load function.
14145          * @param {Object} e The Exception.
14146          */
14147         loadexception : true
14148     });
14149     Roo.data.DataProxy.superclass.constructor.call(this);
14150 };
14151
14152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14153
14154     /**
14155      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14156      */
14157 /*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167 /**
14168  * @class Roo.data.MemoryProxy
14169  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14170  * to the Reader when its load method is called.
14171  * @constructor
14172  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14173  */
14174 Roo.data.MemoryProxy = function(data){
14175     if (data.data) {
14176         data = data.data;
14177     }
14178     Roo.data.MemoryProxy.superclass.constructor.call(this);
14179     this.data = data;
14180 };
14181
14182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14183     
14184     /**
14185      * Load data from the requested source (in this case an in-memory
14186      * data object passed to the constructor), read the data object into
14187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14188      * process that block using the passed callback.
14189      * @param {Object} params This parameter is not used by the MemoryProxy class.
14190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14191      * object into a block of Roo.data.Records.
14192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14193      * The function must be passed <ul>
14194      * <li>The Record block object</li>
14195      * <li>The "arg" argument from the load function</li>
14196      * <li>A boolean success indicator</li>
14197      * </ul>
14198      * @param {Object} scope The scope in which to call the callback
14199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14200      */
14201     load : function(params, reader, callback, scope, arg){
14202         params = params || {};
14203         var result;
14204         try {
14205             result = reader.readRecords(params.data ? params.data :this.data);
14206         }catch(e){
14207             this.fireEvent("loadexception", this, arg, null, e);
14208             callback.call(scope, null, arg, false);
14209             return;
14210         }
14211         callback.call(scope, result, arg, true);
14212     },
14213     
14214     // private
14215     update : function(params, records){
14216         
14217     }
14218 });/*
14219  * Based on:
14220  * Ext JS Library 1.1.1
14221  * Copyright(c) 2006-2007, Ext JS, LLC.
14222  *
14223  * Originally Released Under LGPL - original licence link has changed is not relivant.
14224  *
14225  * Fork - LGPL
14226  * <script type="text/javascript">
14227  */
14228 /**
14229  * @class Roo.data.HttpProxy
14230  * @extends Roo.data.DataProxy
14231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14232  * configured to reference a certain URL.<br><br>
14233  * <p>
14234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14235  * from which the running page was served.<br><br>
14236  * <p>
14237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14238  * <p>
14239  * Be aware that to enable the browser to parse an XML document, the server must set
14240  * the Content-Type header in the HTTP response to "text/xml".
14241  * @constructor
14242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14244  * will be used to make the request.
14245  */
14246 Roo.data.HttpProxy = function(conn){
14247     Roo.data.HttpProxy.superclass.constructor.call(this);
14248     // is conn a conn config or a real conn?
14249     this.conn = conn;
14250     this.useAjax = !conn || !conn.events;
14251   
14252 };
14253
14254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14255     // thse are take from connection...
14256     
14257     /**
14258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14259      */
14260     /**
14261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14262      * extra parameters to each request made by this object. (defaults to undefined)
14263      */
14264     /**
14265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14266      *  to each request made by this object. (defaults to undefined)
14267      */
14268     /**
14269      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
14270      */
14271     /**
14272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14273      */
14274      /**
14275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14276      * @type Boolean
14277      */
14278   
14279
14280     /**
14281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14282      * @type Boolean
14283      */
14284     /**
14285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14287      * a finer-grained basis than the DataProxy events.
14288      */
14289     getConnection : function(){
14290         return this.useAjax ? Roo.Ajax : this.conn;
14291     },
14292
14293     /**
14294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14296      * process that block using the passed callback.
14297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14298      * for the request to the remote server.
14299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14300      * object into a block of Roo.data.Records.
14301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14302      * The function must be passed <ul>
14303      * <li>The Record block object</li>
14304      * <li>The "arg" argument from the load function</li>
14305      * <li>A boolean success indicator</li>
14306      * </ul>
14307      * @param {Object} scope The scope in which to call the callback
14308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14309      */
14310     load : function(params, reader, callback, scope, arg){
14311         if(this.fireEvent("beforeload", this, params) !== false){
14312             var  o = {
14313                 params : params || {},
14314                 request: {
14315                     callback : callback,
14316                     scope : scope,
14317                     arg : arg
14318                 },
14319                 reader: reader,
14320                 callback : this.loadResponse,
14321                 scope: this
14322             };
14323             if(this.useAjax){
14324                 Roo.applyIf(o, this.conn);
14325                 if(this.activeRequest){
14326                     Roo.Ajax.abort(this.activeRequest);
14327                 }
14328                 this.activeRequest = Roo.Ajax.request(o);
14329             }else{
14330                 this.conn.request(o);
14331             }
14332         }else{
14333             callback.call(scope||this, null, arg, false);
14334         }
14335     },
14336
14337     // private
14338     loadResponse : function(o, success, response){
14339         delete this.activeRequest;
14340         if(!success){
14341             this.fireEvent("loadexception", this, o, response);
14342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14343             return;
14344         }
14345         var result;
14346         try {
14347             result = o.reader.read(response);
14348         }catch(e){
14349             this.fireEvent("loadexception", this, o, response, e);
14350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14351             return;
14352         }
14353         
14354         this.fireEvent("load", this, o, o.request.arg);
14355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14356     },
14357
14358     // private
14359     update : function(dataSet){
14360
14361     },
14362
14363     // private
14364     updateResponse : function(dataSet){
14365
14366     }
14367 });/*
14368  * Based on:
14369  * Ext JS Library 1.1.1
14370  * Copyright(c) 2006-2007, Ext JS, LLC.
14371  *
14372  * Originally Released Under LGPL - original licence link has changed is not relivant.
14373  *
14374  * Fork - LGPL
14375  * <script type="text/javascript">
14376  */
14377
14378 /**
14379  * @class Roo.data.ScriptTagProxy
14380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14381  * other than the originating domain of the running page.<br><br>
14382  * <p>
14383  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
14384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14385  * <p>
14386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14387  * source code that is used as the source inside a &lt;script> tag.<br><br>
14388  * <p>
14389  * In order for the browser to process the returned data, the server must wrap the data object
14390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14392  * depending on whether the callback name was passed:
14393  * <p>
14394  * <pre><code>
14395 boolean scriptTag = false;
14396 String cb = request.getParameter("callback");
14397 if (cb != null) {
14398     scriptTag = true;
14399     response.setContentType("text/javascript");
14400 } else {
14401     response.setContentType("application/x-json");
14402 }
14403 Writer out = response.getWriter();
14404 if (scriptTag) {
14405     out.write(cb + "(");
14406 }
14407 out.print(dataBlock.toJsonString());
14408 if (scriptTag) {
14409     out.write(");");
14410 }
14411 </pre></code>
14412  *
14413  * @constructor
14414  * @param {Object} config A configuration object.
14415  */
14416 Roo.data.ScriptTagProxy = function(config){
14417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14418     Roo.apply(this, config);
14419     this.head = document.getElementsByTagName("head")[0];
14420 };
14421
14422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14423
14424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14425     /**
14426      * @cfg {String} url The URL from which to request the data object.
14427      */
14428     /**
14429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14430      */
14431     timeout : 30000,
14432     /**
14433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14434      * the server the name of the callback function set up by the load call to process the returned data object.
14435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14436      * javascript output which calls this named function passing the data object as its only parameter.
14437      */
14438     callbackParam : "callback",
14439     /**
14440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14441      * name to the request.
14442      */
14443     nocache : true,
14444
14445     /**
14446      * Load data from the configured URL, read the data object into
14447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14448      * process that block using the passed callback.
14449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14450      * for the request to the remote server.
14451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14452      * object into a block of Roo.data.Records.
14453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14454      * The function must be passed <ul>
14455      * <li>The Record block object</li>
14456      * <li>The "arg" argument from the load function</li>
14457      * <li>A boolean success indicator</li>
14458      * </ul>
14459      * @param {Object} scope The scope in which to call the callback
14460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14461      */
14462     load : function(params, reader, callback, scope, arg){
14463         if(this.fireEvent("beforeload", this, params) !== false){
14464
14465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14466
14467             var url = this.url;
14468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14469             if(this.nocache){
14470                 url += "&_dc=" + (new Date().getTime());
14471             }
14472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14473             var trans = {
14474                 id : transId,
14475                 cb : "stcCallback"+transId,
14476                 scriptId : "stcScript"+transId,
14477                 params : params,
14478                 arg : arg,
14479                 url : url,
14480                 callback : callback,
14481                 scope : scope,
14482                 reader : reader
14483             };
14484             var conn = this;
14485
14486             window[trans.cb] = function(o){
14487                 conn.handleResponse(o, trans);
14488             };
14489
14490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14491
14492             if(this.autoAbort !== false){
14493                 this.abort();
14494             }
14495
14496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14497
14498             var script = document.createElement("script");
14499             script.setAttribute("src", url);
14500             script.setAttribute("type", "text/javascript");
14501             script.setAttribute("id", trans.scriptId);
14502             this.head.appendChild(script);
14503
14504             this.trans = trans;
14505         }else{
14506             callback.call(scope||this, null, arg, false);
14507         }
14508     },
14509
14510     // private
14511     isLoading : function(){
14512         return this.trans ? true : false;
14513     },
14514
14515     /**
14516      * Abort the current server request.
14517      */
14518     abort : function(){
14519         if(this.isLoading()){
14520             this.destroyTrans(this.trans);
14521         }
14522     },
14523
14524     // private
14525     destroyTrans : function(trans, isLoaded){
14526         this.head.removeChild(document.getElementById(trans.scriptId));
14527         clearTimeout(trans.timeoutId);
14528         if(isLoaded){
14529             window[trans.cb] = undefined;
14530             try{
14531                 delete window[trans.cb];
14532             }catch(e){}
14533         }else{
14534             // if hasn't been loaded, wait for load to remove it to prevent script error
14535             window[trans.cb] = function(){
14536                 window[trans.cb] = undefined;
14537                 try{
14538                     delete window[trans.cb];
14539                 }catch(e){}
14540             };
14541         }
14542     },
14543
14544     // private
14545     handleResponse : function(o, trans){
14546         this.trans = false;
14547         this.destroyTrans(trans, true);
14548         var result;
14549         try {
14550             result = trans.reader.readRecords(o);
14551         }catch(e){
14552             this.fireEvent("loadexception", this, o, trans.arg, e);
14553             trans.callback.call(trans.scope||window, null, trans.arg, false);
14554             return;
14555         }
14556         this.fireEvent("load", this, o, trans.arg);
14557         trans.callback.call(trans.scope||window, result, trans.arg, true);
14558     },
14559
14560     // private
14561     handleFailure : function(trans){
14562         this.trans = false;
14563         this.destroyTrans(trans, false);
14564         this.fireEvent("loadexception", this, null, trans.arg);
14565         trans.callback.call(trans.scope||window, null, trans.arg, false);
14566     }
14567 });/*
14568  * Based on:
14569  * Ext JS Library 1.1.1
14570  * Copyright(c) 2006-2007, Ext JS, LLC.
14571  *
14572  * Originally Released Under LGPL - original licence link has changed is not relivant.
14573  *
14574  * Fork - LGPL
14575  * <script type="text/javascript">
14576  */
14577
14578 /**
14579  * @class Roo.data.JsonReader
14580  * @extends Roo.data.DataReader
14581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14582  * based on mappings in a provided Roo.data.Record constructor.
14583  * 
14584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14585  * in the reply previously. 
14586  * 
14587  * <p>
14588  * Example code:
14589  * <pre><code>
14590 var RecordDef = Roo.data.Record.create([
14591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14593 ]);
14594 var myReader = new Roo.data.JsonReader({
14595     totalProperty: "results",    // The property which contains the total dataset size (optional)
14596     root: "rows",                // The property which contains an Array of row objects
14597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14598 }, RecordDef);
14599 </code></pre>
14600  * <p>
14601  * This would consume a JSON file like this:
14602  * <pre><code>
14603 { 'results': 2, 'rows': [
14604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14606 }
14607 </code></pre>
14608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14610  * paged from the remote server.
14611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14612  * @cfg {String} root name of the property which contains the Array of row objects.
14613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14614  * @cfg {Array} fields Array of field definition objects
14615  * @constructor
14616  * Create a new JsonReader
14617  * @param {Object} meta Metadata configuration options
14618  * @param {Object} recordType Either an Array of field definition objects,
14619  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14620  */
14621 Roo.data.JsonReader = function(meta, recordType){
14622     
14623     meta = meta || {};
14624     // set some defaults:
14625     Roo.applyIf(meta, {
14626         totalProperty: 'total',
14627         successProperty : 'success',
14628         root : 'data',
14629         id : 'id'
14630     });
14631     
14632     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14633 };
14634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14635     
14636     readerType : 'Json',
14637     
14638     /**
14639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14640      * Used by Store query builder to append _requestMeta to params.
14641      * 
14642      */
14643     metaFromRemote : false,
14644     /**
14645      * This method is only used by a DataProxy which has retrieved data from a remote server.
14646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14647      * @return {Object} data A data block which is used by an Roo.data.Store object as
14648      * a cache of Roo.data.Records.
14649      */
14650     read : function(response){
14651         var json = response.responseText;
14652        
14653         var o = /* eval:var:o */ eval("("+json+")");
14654         if(!o) {
14655             throw {message: "JsonReader.read: Json object not found"};
14656         }
14657         
14658         if(o.metaData){
14659             
14660             delete this.ef;
14661             this.metaFromRemote = true;
14662             this.meta = o.metaData;
14663             this.recordType = Roo.data.Record.create(o.metaData.fields);
14664             this.onMetaChange(this.meta, this.recordType, o);
14665         }
14666         return this.readRecords(o);
14667     },
14668
14669     // private function a store will implement
14670     onMetaChange : function(meta, recordType, o){
14671
14672     },
14673
14674     /**
14675          * @ignore
14676          */
14677     simpleAccess: function(obj, subsc) {
14678         return obj[subsc];
14679     },
14680
14681         /**
14682          * @ignore
14683          */
14684     getJsonAccessor: function(){
14685         var re = /[\[\.]/;
14686         return function(expr) {
14687             try {
14688                 return(re.test(expr))
14689                     ? new Function("obj", "return obj." + expr)
14690                     : function(obj){
14691                         return obj[expr];
14692                     };
14693             } catch(e){}
14694             return Roo.emptyFn;
14695         };
14696     }(),
14697
14698     /**
14699      * Create a data block containing Roo.data.Records from an XML document.
14700      * @param {Object} o An object which contains an Array of row objects in the property specified
14701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14702      * which contains the total size of the dataset.
14703      * @return {Object} data A data block which is used by an Roo.data.Store object as
14704      * a cache of Roo.data.Records.
14705      */
14706     readRecords : function(o){
14707         /**
14708          * After any data loads, the raw JSON data is available for further custom processing.
14709          * @type Object
14710          */
14711         this.o = o;
14712         var s = this.meta, Record = this.recordType,
14713             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14714
14715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14716         if (!this.ef) {
14717             if(s.totalProperty) {
14718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14719                 }
14720                 if(s.successProperty) {
14721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14722                 }
14723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14724                 if (s.id) {
14725                         var g = this.getJsonAccessor(s.id);
14726                         this.getId = function(rec) {
14727                                 var r = g(rec);  
14728                                 return (r === undefined || r === "") ? null : r;
14729                         };
14730                 } else {
14731                         this.getId = function(){return null;};
14732                 }
14733             this.ef = [];
14734             for(var jj = 0; jj < fl; jj++){
14735                 f = fi[jj];
14736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14737                 this.ef[jj] = this.getJsonAccessor(map);
14738             }
14739         }
14740
14741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14742         if(s.totalProperty){
14743             var vt = parseInt(this.getTotal(o), 10);
14744             if(!isNaN(vt)){
14745                 totalRecords = vt;
14746             }
14747         }
14748         if(s.successProperty){
14749             var vs = this.getSuccess(o);
14750             if(vs === false || vs === 'false'){
14751                 success = false;
14752             }
14753         }
14754         var records = [];
14755         for(var i = 0; i < c; i++){
14756                 var n = root[i];
14757             var values = {};
14758             var id = this.getId(n);
14759             for(var j = 0; j < fl; j++){
14760                 f = fi[j];
14761             var v = this.ef[j](n);
14762             if (!f.convert) {
14763                 Roo.log('missing convert for ' + f.name);
14764                 Roo.log(f);
14765                 continue;
14766             }
14767             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14768             }
14769             var record = new Record(values, id);
14770             record.json = n;
14771             records[i] = record;
14772         }
14773         return {
14774             raw : o,
14775             success : success,
14776             records : records,
14777             totalRecords : totalRecords
14778         };
14779     },
14780     // used when loading children.. @see loadDataFromChildren
14781     toLoadData: function(rec)
14782     {
14783         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14784         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14785         return { data : data, total : data.length };
14786         
14787     }
14788 });/*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798
14799 /**
14800  * @class Roo.data.ArrayReader
14801  * @extends Roo.data.DataReader
14802  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14803  * Each element of that Array represents a row of data fields. The
14804  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14805  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14806  * <p>
14807  * Example code:.
14808  * <pre><code>
14809 var RecordDef = Roo.data.Record.create([
14810     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14811     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14812 ]);
14813 var myReader = new Roo.data.ArrayReader({
14814     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14815 }, RecordDef);
14816 </code></pre>
14817  * <p>
14818  * This would consume an Array like this:
14819  * <pre><code>
14820 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14821   </code></pre>
14822  
14823  * @constructor
14824  * Create a new JsonReader
14825  * @param {Object} meta Metadata configuration options.
14826  * @param {Object|Array} recordType Either an Array of field definition objects
14827  * 
14828  * @cfg {Array} fields Array of field definition objects
14829  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14830  * as specified to {@link Roo.data.Record#create},
14831  * or an {@link Roo.data.Record} object
14832  *
14833  * 
14834  * created using {@link Roo.data.Record#create}.
14835  */
14836 Roo.data.ArrayReader = function(meta, recordType)
14837 {    
14838     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14839 };
14840
14841 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14842     
14843       /**
14844      * Create a data block containing Roo.data.Records from an XML document.
14845      * @param {Object} o An Array of row objects which represents the dataset.
14846      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14847      * a cache of Roo.data.Records.
14848      */
14849     readRecords : function(o)
14850     {
14851         var sid = this.meta ? this.meta.id : null;
14852         var recordType = this.recordType, fields = recordType.prototype.fields;
14853         var records = [];
14854         var root = o;
14855         for(var i = 0; i < root.length; i++){
14856                 var n = root[i];
14857             var values = {};
14858             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14859             for(var j = 0, jlen = fields.length; j < jlen; j++){
14860                 var f = fields.items[j];
14861                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14862                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14863                 v = f.convert(v);
14864                 values[f.name] = v;
14865             }
14866             var record = new recordType(values, id);
14867             record.json = n;
14868             records[records.length] = record;
14869         }
14870         return {
14871             records : records,
14872             totalRecords : records.length
14873         };
14874     },
14875     // used when loading children.. @see loadDataFromChildren
14876     toLoadData: function(rec)
14877     {
14878         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14879         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14880         
14881     }
14882     
14883     
14884 });/*
14885  * - LGPL
14886  * * 
14887  */
14888
14889 /**
14890  * @class Roo.bootstrap.ComboBox
14891  * @extends Roo.bootstrap.TriggerField
14892  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14893  * @cfg {Boolean} append (true|false) default false
14894  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14895  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14896  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14897  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14898  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14899  * @cfg {Boolean} animate default true
14900  * @cfg {Boolean} emptyResultText only for touch device
14901  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14902  * @cfg {String} emptyTitle default ''
14903  * @cfg {Number} width fixed with? experimental
14904  * @constructor
14905  * Create a new ComboBox.
14906  * @param {Object} config Configuration options
14907  */
14908 Roo.bootstrap.ComboBox = function(config){
14909     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14910     this.addEvents({
14911         /**
14912          * @event expand
14913          * Fires when the dropdown list is expanded
14914         * @param {Roo.bootstrap.ComboBox} combo This combo box
14915         */
14916         'expand' : true,
14917         /**
14918          * @event collapse
14919          * Fires when the dropdown list is collapsed
14920         * @param {Roo.bootstrap.ComboBox} combo This combo box
14921         */
14922         'collapse' : true,
14923         /**
14924          * @event beforeselect
14925          * Fires before a list item is selected. Return false to cancel the selection.
14926         * @param {Roo.bootstrap.ComboBox} combo This combo box
14927         * @param {Roo.data.Record} record The data record returned from the underlying store
14928         * @param {Number} index The index of the selected item in the dropdown list
14929         */
14930         'beforeselect' : true,
14931         /**
14932          * @event select
14933          * Fires when a list item is selected
14934         * @param {Roo.bootstrap.ComboBox} combo This combo box
14935         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14936         * @param {Number} index The index of the selected item in the dropdown list
14937         */
14938         'select' : true,
14939         /**
14940          * @event beforequery
14941          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14942          * The event object passed has these properties:
14943         * @param {Roo.bootstrap.ComboBox} combo This combo box
14944         * @param {String} query The query
14945         * @param {Boolean} forceAll true to force "all" query
14946         * @param {Boolean} cancel true to cancel the query
14947         * @param {Object} e The query event object
14948         */
14949         'beforequery': true,
14950          /**
14951          * @event add
14952          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14953         * @param {Roo.bootstrap.ComboBox} combo This combo box
14954         */
14955         'add' : true,
14956         /**
14957          * @event edit
14958          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14959         * @param {Roo.bootstrap.ComboBox} combo This combo box
14960         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14961         */
14962         'edit' : true,
14963         /**
14964          * @event remove
14965          * Fires when the remove value from the combobox array
14966         * @param {Roo.bootstrap.ComboBox} combo This combo box
14967         */
14968         'remove' : true,
14969         /**
14970          * @event afterremove
14971          * Fires when the remove value from the combobox array
14972         * @param {Roo.bootstrap.ComboBox} combo This combo box
14973         */
14974         'afterremove' : true,
14975         /**
14976          * @event specialfilter
14977          * Fires when specialfilter
14978             * @param {Roo.bootstrap.ComboBox} combo This combo box
14979             */
14980         'specialfilter' : true,
14981         /**
14982          * @event tick
14983          * Fires when tick the element
14984             * @param {Roo.bootstrap.ComboBox} combo This combo box
14985             */
14986         'tick' : true,
14987         /**
14988          * @event touchviewdisplay
14989          * Fires when touch view require special display (default is using displayField)
14990             * @param {Roo.bootstrap.ComboBox} combo This combo box
14991             * @param {Object} cfg set html .
14992             */
14993         'touchviewdisplay' : true
14994         
14995     });
14996     
14997     this.item = [];
14998     this.tickItems = [];
14999     
15000     this.selectedIndex = -1;
15001     if(this.mode == 'local'){
15002         if(config.queryDelay === undefined){
15003             this.queryDelay = 10;
15004         }
15005         if(config.minChars === undefined){
15006             this.minChars = 0;
15007         }
15008     }
15009 };
15010
15011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15012      
15013     /**
15014      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15015      * rendering into an Roo.Editor, defaults to false)
15016      */
15017     /**
15018      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15019      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15020      */
15021     /**
15022      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15023      */
15024     /**
15025      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15026      * the dropdown list (defaults to undefined, with no header element)
15027      */
15028
15029      /**
15030      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15031      */
15032      
15033      /**
15034      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15035      */
15036     listWidth: undefined,
15037     /**
15038      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15039      * mode = 'remote' or 'text' if mode = 'local')
15040      */
15041     displayField: undefined,
15042     
15043     /**
15044      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15045      * mode = 'remote' or 'value' if mode = 'local'). 
15046      * Note: use of a valueField requires the user make a selection
15047      * in order for a value to be mapped.
15048      */
15049     valueField: undefined,
15050     /**
15051      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15052      */
15053     modalTitle : '',
15054     
15055     /**
15056      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15057      * field's data value (defaults to the underlying DOM element's name)
15058      */
15059     hiddenName: undefined,
15060     /**
15061      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15062      */
15063     listClass: '',
15064     /**
15065      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15066      */
15067     selectedClass: 'active',
15068     
15069     /**
15070      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15071      */
15072     shadow:'sides',
15073     /**
15074      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15075      * anchor positions (defaults to 'tl-bl')
15076      */
15077     listAlign: 'tl-bl?',
15078     /**
15079      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15080      */
15081     maxHeight: 300,
15082     /**
15083      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15084      * query specified by the allQuery config option (defaults to 'query')
15085      */
15086     triggerAction: 'query',
15087     /**
15088      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15089      * (defaults to 4, does not apply if editable = false)
15090      */
15091     minChars : 4,
15092     /**
15093      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15094      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15095      */
15096     typeAhead: false,
15097     /**
15098      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15099      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15100      */
15101     queryDelay: 500,
15102     /**
15103      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15104      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15105      */
15106     pageSize: 0,
15107     /**
15108      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15109      * when editable = true (defaults to false)
15110      */
15111     selectOnFocus:false,
15112     /**
15113      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15114      */
15115     queryParam: 'query',
15116     /**
15117      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15118      * when mode = 'remote' (defaults to 'Loading...')
15119      */
15120     loadingText: 'Loading...',
15121     /**
15122      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15123      */
15124     resizable: false,
15125     /**
15126      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15127      */
15128     handleHeight : 8,
15129     /**
15130      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15131      * traditional select (defaults to true)
15132      */
15133     editable: true,
15134     /**
15135      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15136      */
15137     allQuery: '',
15138     /**
15139      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15140      */
15141     mode: 'remote',
15142     /**
15143      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15144      * listWidth has a higher value)
15145      */
15146     minListWidth : 70,
15147     /**
15148      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15149      * allow the user to set arbitrary text into the field (defaults to false)
15150      */
15151     forceSelection:false,
15152     /**
15153      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15154      * if typeAhead = true (defaults to 250)
15155      */
15156     typeAheadDelay : 250,
15157     /**
15158      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15159      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15160      */
15161     valueNotFoundText : undefined,
15162     /**
15163      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15164      */
15165     blockFocus : false,
15166     
15167     /**
15168      * @cfg {Boolean} disableClear Disable showing of clear button.
15169      */
15170     disableClear : false,
15171     /**
15172      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15173      */
15174     alwaysQuery : false,
15175     
15176     /**
15177      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15178      */
15179     multiple : false,
15180     
15181     /**
15182      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15183      */
15184     invalidClass : "has-warning",
15185     
15186     /**
15187      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     validClass : "has-success",
15190     
15191     /**
15192      * @cfg {Boolean} specialFilter (true|false) special filter default false
15193      */
15194     specialFilter : false,
15195     
15196     /**
15197      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15198      */
15199     mobileTouchView : true,
15200     
15201     /**
15202      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15203      */
15204     useNativeIOS : false,
15205     
15206     /**
15207      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15208      */
15209     mobile_restrict_height : false,
15210     
15211     ios_options : false,
15212     
15213     //private
15214     addicon : false,
15215     editicon: false,
15216     
15217     page: 0,
15218     hasQuery: false,
15219     append: false,
15220     loadNext: false,
15221     autoFocus : true,
15222     tickable : false,
15223     btnPosition : 'right',
15224     triggerList : true,
15225     showToggleBtn : true,
15226     animate : true,
15227     emptyResultText: 'Empty',
15228     triggerText : 'Select',
15229     emptyTitle : '',
15230     width : false,
15231     
15232     // element that contains real text value.. (when hidden is used..)
15233     
15234     getAutoCreate : function()
15235     {   
15236         var cfg = false;
15237         //render
15238         /*
15239          * Render classic select for iso
15240          */
15241         
15242         if(Roo.isIOS && this.useNativeIOS){
15243             cfg = this.getAutoCreateNativeIOS();
15244             return cfg;
15245         }
15246         
15247         /*
15248          * Touch Devices
15249          */
15250         
15251         if(Roo.isTouch && this.mobileTouchView){
15252             cfg = this.getAutoCreateTouchView();
15253             return cfg;;
15254         }
15255         
15256         /*
15257          *  Normal ComboBox
15258          */
15259         if(!this.tickable){
15260             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15261             return cfg;
15262         }
15263         
15264         /*
15265          *  ComboBox with tickable selections
15266          */
15267              
15268         var align = this.labelAlign || this.parentLabelAlign();
15269         
15270         cfg = {
15271             cls : 'form-group roo-combobox-tickable' //input-group
15272         };
15273         
15274         var btn_text_select = '';
15275         var btn_text_done = '';
15276         var btn_text_cancel = '';
15277         
15278         if (this.btn_text_show) {
15279             btn_text_select = 'Select';
15280             btn_text_done = 'Done';
15281             btn_text_cancel = 'Cancel'; 
15282         }
15283         
15284         var buttons = {
15285             tag : 'div',
15286             cls : 'tickable-buttons',
15287             cn : [
15288                 {
15289                     tag : 'button',
15290                     type : 'button',
15291                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15292                     //html : this.triggerText
15293                     html: btn_text_select
15294                 },
15295                 {
15296                     tag : 'button',
15297                     type : 'button',
15298                     name : 'ok',
15299                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15300                     //html : 'Done'
15301                     html: btn_text_done
15302                 },
15303                 {
15304                     tag : 'button',
15305                     type : 'button',
15306                     name : 'cancel',
15307                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15308                     //html : 'Cancel'
15309                     html: btn_text_cancel
15310                 }
15311             ]
15312         };
15313         
15314         if(this.editable){
15315             buttons.cn.unshift({
15316                 tag: 'input',
15317                 cls: 'roo-select2-search-field-input'
15318             });
15319         }
15320         
15321         var _this = this;
15322         
15323         Roo.each(buttons.cn, function(c){
15324             if (_this.size) {
15325                 c.cls += ' btn-' + _this.size;
15326             }
15327
15328             if (_this.disabled) {
15329                 c.disabled = true;
15330             }
15331         });
15332         
15333         var box = {
15334             tag: 'div',
15335             style : 'display: contents',
15336             cn: [
15337                 {
15338                     tag: 'input',
15339                     type : 'hidden',
15340                     cls: 'form-hidden-field'
15341                 },
15342                 {
15343                     tag: 'ul',
15344                     cls: 'roo-select2-choices',
15345                     cn:[
15346                         {
15347                             tag: 'li',
15348                             cls: 'roo-select2-search-field',
15349                             cn: [
15350                                 buttons
15351                             ]
15352                         }
15353                     ]
15354                 }
15355             ]
15356         };
15357         
15358         var combobox = {
15359             cls: 'roo-select2-container input-group roo-select2-container-multi',
15360             cn: [
15361                 
15362                 box
15363 //                {
15364 //                    tag: 'ul',
15365 //                    cls: 'typeahead typeahead-long dropdown-menu',
15366 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15367 //                }
15368             ]
15369         };
15370         
15371         if(this.hasFeedback && !this.allowBlank){
15372             
15373             var feedback = {
15374                 tag: 'span',
15375                 cls: 'glyphicon form-control-feedback'
15376             };
15377
15378             combobox.cn.push(feedback);
15379         }
15380         
15381         
15382         
15383         var indicator = {
15384             tag : 'i',
15385             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15386             tooltip : 'This field is required'
15387         };
15388         if (Roo.bootstrap.version == 4) {
15389             indicator = {
15390                 tag : 'i',
15391                 style : 'display:none'
15392             };
15393         }
15394         if (align ==='left' && this.fieldLabel.length) {
15395             
15396             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15397             
15398             cfg.cn = [
15399                 indicator,
15400                 {
15401                     tag: 'label',
15402                     'for' :  id,
15403                     cls : 'control-label col-form-label',
15404                     html : this.fieldLabel
15405
15406                 },
15407                 {
15408                     cls : "", 
15409                     cn: [
15410                         combobox
15411                     ]
15412                 }
15413
15414             ];
15415             
15416             var labelCfg = cfg.cn[1];
15417             var contentCfg = cfg.cn[2];
15418             
15419
15420             if(this.indicatorpos == 'right'){
15421                 
15422                 cfg.cn = [
15423                     {
15424                         tag: 'label',
15425                         'for' :  id,
15426                         cls : 'control-label col-form-label',
15427                         cn : [
15428                             {
15429                                 tag : 'span',
15430                                 html : this.fieldLabel
15431                             },
15432                             indicator
15433                         ]
15434                     },
15435                     {
15436                         cls : "",
15437                         cn: [
15438                             combobox
15439                         ]
15440                     }
15441
15442                 ];
15443                 
15444                 
15445                 
15446                 labelCfg = cfg.cn[0];
15447                 contentCfg = cfg.cn[1];
15448             
15449             }
15450             
15451             if(this.labelWidth > 12){
15452                 labelCfg.style = "width: " + this.labelWidth + 'px';
15453             }
15454             if(this.width * 1 > 0){
15455                 contentCfg.style = "width: " + this.width + 'px';
15456             }
15457             if(this.labelWidth < 13 && this.labelmd == 0){
15458                 this.labelmd = this.labelWidth;
15459             }
15460             
15461             if(this.labellg > 0){
15462                 labelCfg.cls += ' col-lg-' + this.labellg;
15463                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15464             }
15465             
15466             if(this.labelmd > 0){
15467                 labelCfg.cls += ' col-md-' + this.labelmd;
15468                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15469             }
15470             
15471             if(this.labelsm > 0){
15472                 labelCfg.cls += ' col-sm-' + this.labelsm;
15473                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15474             }
15475             
15476             if(this.labelxs > 0){
15477                 labelCfg.cls += ' col-xs-' + this.labelxs;
15478                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15479             }
15480                 
15481                 
15482         } else if ( this.fieldLabel.length) {
15483 //                Roo.log(" label");
15484                  cfg.cn = [
15485                    indicator,
15486                     {
15487                         tag: 'label',
15488                         //cls : 'input-group-addon',
15489                         html : this.fieldLabel
15490                     },
15491                     combobox
15492                 ];
15493                 
15494                 if(this.indicatorpos == 'right'){
15495                     cfg.cn = [
15496                         {
15497                             tag: 'label',
15498                             //cls : 'input-group-addon',
15499                             html : this.fieldLabel
15500                         },
15501                         indicator,
15502                         combobox
15503                     ];
15504                     
15505                 }
15506
15507         } else {
15508             
15509 //                Roo.log(" no label && no align");
15510                 cfg = combobox
15511                      
15512                 
15513         }
15514          
15515         var settings=this;
15516         ['xs','sm','md','lg'].map(function(size){
15517             if (settings[size]) {
15518                 cfg.cls += ' col-' + size + '-' + settings[size];
15519             }
15520         });
15521         
15522         return cfg;
15523         
15524     },
15525     
15526     _initEventsCalled : false,
15527     
15528     // private
15529     initEvents: function()
15530     {   
15531         if (this._initEventsCalled) { // as we call render... prevent looping...
15532             return;
15533         }
15534         this._initEventsCalled = true;
15535         
15536         if (!this.store) {
15537             throw "can not find store for combo";
15538         }
15539         
15540         this.indicator = this.indicatorEl();
15541         
15542         this.store = Roo.factory(this.store, Roo.data);
15543         this.store.parent = this;
15544         
15545         // if we are building from html. then this element is so complex, that we can not really
15546         // use the rendered HTML.
15547         // so we have to trash and replace the previous code.
15548         if (Roo.XComponent.build_from_html) {
15549             // remove this element....
15550             var e = this.el.dom, k=0;
15551             while (e ) { e = e.previousSibling;  ++k;}
15552
15553             this.el.remove();
15554             
15555             this.el=false;
15556             this.rendered = false;
15557             
15558             this.render(this.parent().getChildContainer(true), k);
15559         }
15560         
15561         if(Roo.isIOS && this.useNativeIOS){
15562             this.initIOSView();
15563             return;
15564         }
15565         
15566         /*
15567          * Touch Devices
15568          */
15569         
15570         if(Roo.isTouch && this.mobileTouchView){
15571             this.initTouchView();
15572             return;
15573         }
15574         
15575         if(this.tickable){
15576             this.initTickableEvents();
15577             return;
15578         }
15579         
15580         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15581         
15582         if(this.hiddenName){
15583             
15584             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15585             
15586             this.hiddenField.dom.value =
15587                 this.hiddenValue !== undefined ? this.hiddenValue :
15588                 this.value !== undefined ? this.value : '';
15589
15590             // prevent input submission
15591             this.el.dom.removeAttribute('name');
15592             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15593              
15594              
15595         }
15596         //if(Roo.isGecko){
15597         //    this.el.dom.setAttribute('autocomplete', 'off');
15598         //}
15599         
15600         var cls = 'x-combo-list';
15601         
15602         //this.list = new Roo.Layer({
15603         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15604         //});
15605         
15606         var _this = this;
15607         
15608         (function(){
15609             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15610             _this.list.setWidth(lw);
15611         }).defer(100);
15612         
15613         this.list.on('mouseover', this.onViewOver, this);
15614         this.list.on('mousemove', this.onViewMove, this);
15615         this.list.on('scroll', this.onViewScroll, this);
15616         
15617         /*
15618         this.list.swallowEvent('mousewheel');
15619         this.assetHeight = 0;
15620
15621         if(this.title){
15622             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15623             this.assetHeight += this.header.getHeight();
15624         }
15625
15626         this.innerList = this.list.createChild({cls:cls+'-inner'});
15627         this.innerList.on('mouseover', this.onViewOver, this);
15628         this.innerList.on('mousemove', this.onViewMove, this);
15629         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15630         
15631         if(this.allowBlank && !this.pageSize && !this.disableClear){
15632             this.footer = this.list.createChild({cls:cls+'-ft'});
15633             this.pageTb = new Roo.Toolbar(this.footer);
15634            
15635         }
15636         if(this.pageSize){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15639                     {pageSize: this.pageSize});
15640             
15641         }
15642         
15643         if (this.pageTb && this.allowBlank && !this.disableClear) {
15644             var _this = this;
15645             this.pageTb.add(new Roo.Toolbar.Fill(), {
15646                 cls: 'x-btn-icon x-btn-clear',
15647                 text: '&#160;',
15648                 handler: function()
15649                 {
15650                     _this.collapse();
15651                     _this.clearValue();
15652                     _this.onSelect(false, -1);
15653                 }
15654             });
15655         }
15656         if (this.footer) {
15657             this.assetHeight += this.footer.getHeight();
15658         }
15659         */
15660             
15661         if(!this.tpl){
15662             this.tpl = Roo.bootstrap.version == 4 ?
15663                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15664                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15665         }
15666
15667         this.view = new Roo.View(this.list, this.tpl, {
15668             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15669         });
15670         //this.view.wrapEl.setDisplayed(false);
15671         this.view.on('click', this.onViewClick, this);
15672         
15673         
15674         this.store.on('beforeload', this.onBeforeLoad, this);
15675         this.store.on('load', this.onLoad, this);
15676         this.store.on('loadexception', this.onLoadException, this);
15677         /*
15678         if(this.resizable){
15679             this.resizer = new Roo.Resizable(this.list,  {
15680                pinned:true, handles:'se'
15681             });
15682             this.resizer.on('resize', function(r, w, h){
15683                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15684                 this.listWidth = w;
15685                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15686                 this.restrictHeight();
15687             }, this);
15688             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15689         }
15690         */
15691         if(!this.editable){
15692             this.editable = true;
15693             this.setEditable(false);
15694         }
15695         
15696         /*
15697         
15698         if (typeof(this.events.add.listeners) != 'undefined') {
15699             
15700             this.addicon = this.wrap.createChild(
15701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15702        
15703             this.addicon.on('click', function(e) {
15704                 this.fireEvent('add', this);
15705             }, this);
15706         }
15707         if (typeof(this.events.edit.listeners) != 'undefined') {
15708             
15709             this.editicon = this.wrap.createChild(
15710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15711             if (this.addicon) {
15712                 this.editicon.setStyle('margin-left', '40px');
15713             }
15714             this.editicon.on('click', function(e) {
15715                 
15716                 // we fire even  if inothing is selected..
15717                 this.fireEvent('edit', this, this.lastData );
15718                 
15719             }, this);
15720         }
15721         */
15722         
15723         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15724             "up" : function(e){
15725                 this.inKeyMode = true;
15726                 this.selectPrev();
15727             },
15728
15729             "down" : function(e){
15730                 if(!this.isExpanded()){
15731                     this.onTriggerClick();
15732                 }else{
15733                     this.inKeyMode = true;
15734                     this.selectNext();
15735                 }
15736             },
15737
15738             "enter" : function(e){
15739 //                this.onViewClick();
15740                 //return true;
15741                 this.collapse();
15742                 
15743                 if(this.fireEvent("specialkey", this, e)){
15744                     this.onViewClick(false);
15745                 }
15746                 
15747                 return true;
15748             },
15749
15750             "esc" : function(e){
15751                 this.collapse();
15752             },
15753
15754             "tab" : function(e){
15755                 this.collapse();
15756                 
15757                 if(this.fireEvent("specialkey", this, e)){
15758                     this.onViewClick(false);
15759                 }
15760                 
15761                 return true;
15762             },
15763
15764             scope : this,
15765
15766             doRelay : function(foo, bar, hname){
15767                 if(hname == 'down' || this.scope.isExpanded()){
15768                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15769                 }
15770                 return true;
15771             },
15772
15773             forceKeyDown: true
15774         });
15775         
15776         
15777         this.queryDelay = Math.max(this.queryDelay || 10,
15778                 this.mode == 'local' ? 10 : 250);
15779         
15780         
15781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15782         
15783         if(this.typeAhead){
15784             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15785         }
15786         if(this.editable !== false){
15787             this.inputEl().on("keyup", this.onKeyUp, this);
15788         }
15789         if(this.forceSelection){
15790             this.inputEl().on('blur', this.doForce, this);
15791         }
15792         
15793         if(this.multiple){
15794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15796         }
15797     },
15798     
15799     initTickableEvents: function()
15800     {   
15801         this.createList();
15802         
15803         if(this.hiddenName){
15804             
15805             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15806             
15807             this.hiddenField.dom.value =
15808                 this.hiddenValue !== undefined ? this.hiddenValue :
15809                 this.value !== undefined ? this.value : '';
15810
15811             // prevent input submission
15812             this.el.dom.removeAttribute('name');
15813             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15814              
15815              
15816         }
15817         
15818 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15819         
15820         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15821         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15822         if(this.triggerList){
15823             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15824         }
15825          
15826         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15827         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15828         
15829         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15830         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15831         
15832         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15833         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15834         
15835         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15836         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15837         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15838         
15839         this.okBtn.hide();
15840         this.cancelBtn.hide();
15841         
15842         var _this = this;
15843         
15844         (function(){
15845             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15846             _this.list.setWidth(lw);
15847         }).defer(100);
15848         
15849         this.list.on('mouseover', this.onViewOver, this);
15850         this.list.on('mousemove', this.onViewMove, this);
15851         
15852         this.list.on('scroll', this.onViewScroll, this);
15853         
15854         if(!this.tpl){
15855             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15856                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15857         }
15858
15859         this.view = new Roo.View(this.list, this.tpl, {
15860             singleSelect:true,
15861             tickable:true,
15862             parent:this,
15863             store: this.store,
15864             selectedClass: this.selectedClass
15865         });
15866         
15867         //this.view.wrapEl.setDisplayed(false);
15868         this.view.on('click', this.onViewClick, this);
15869         
15870         
15871         
15872         this.store.on('beforeload', this.onBeforeLoad, this);
15873         this.store.on('load', this.onLoad, this);
15874         this.store.on('loadexception', this.onLoadException, this);
15875         
15876         if(this.editable){
15877             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15878                 "up" : function(e){
15879                     this.inKeyMode = true;
15880                     this.selectPrev();
15881                 },
15882
15883                 "down" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectNext();
15886                 },
15887
15888                 "enter" : function(e){
15889                     if(this.fireEvent("specialkey", this, e)){
15890                         this.onViewClick(false);
15891                     }
15892                     
15893                     return true;
15894                 },
15895
15896                 "esc" : function(e){
15897                     this.onTickableFooterButtonClick(e, false, false);
15898                 },
15899
15900                 "tab" : function(e){
15901                     this.fireEvent("specialkey", this, e);
15902                     
15903                     this.onTickableFooterButtonClick(e, false, false);
15904                     
15905                     return true;
15906                 },
15907
15908                 scope : this,
15909
15910                 doRelay : function(e, fn, key){
15911                     if(this.scope.isExpanded()){
15912                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15913                     }
15914                     return true;
15915                 },
15916
15917                 forceKeyDown: true
15918             });
15919         }
15920         
15921         this.queryDelay = Math.max(this.queryDelay || 10,
15922                 this.mode == 'local' ? 10 : 250);
15923         
15924         
15925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15926         
15927         if(this.typeAhead){
15928             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15929         }
15930         
15931         if(this.editable !== false){
15932             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15933         }
15934         
15935         this.indicator = this.indicatorEl();
15936         
15937         if(this.indicator){
15938             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15939             this.indicator.hide();
15940         }
15941         
15942     },
15943
15944     onDestroy : function(){
15945         if(this.view){
15946             this.view.setStore(null);
15947             this.view.el.removeAllListeners();
15948             this.view.el.remove();
15949             this.view.purgeListeners();
15950         }
15951         if(this.list){
15952             this.list.dom.innerHTML  = '';
15953         }
15954         
15955         if(this.store){
15956             this.store.un('beforeload', this.onBeforeLoad, this);
15957             this.store.un('load', this.onLoad, this);
15958             this.store.un('loadexception', this.onLoadException, this);
15959         }
15960         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15961     },
15962
15963     // private
15964     fireKey : function(e){
15965         if(e.isNavKeyPress() && !this.list.isVisible()){
15966             this.fireEvent("specialkey", this, e);
15967         }
15968     },
15969
15970     // private
15971     onResize: function(w, h)
15972     {
15973         
15974         
15975 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15976 //        
15977 //        if(typeof w != 'number'){
15978 //            // we do not handle it!?!?
15979 //            return;
15980 //        }
15981 //        var tw = this.trigger.getWidth();
15982 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15983 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15984 //        var x = w - tw;
15985 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15986 //            
15987 //        //this.trigger.setStyle('left', x+'px');
15988 //        
15989 //        if(this.list && this.listWidth === undefined){
15990 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15991 //            this.list.setWidth(lw);
15992 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15993 //        }
15994         
15995     
15996         
15997     },
15998
15999     /**
16000      * Allow or prevent the user from directly editing the field text.  If false is passed,
16001      * the user will only be able to select from the items defined in the dropdown list.  This method
16002      * is the runtime equivalent of setting the 'editable' config option at config time.
16003      * @param {Boolean} value True to allow the user to directly edit the field text
16004      */
16005     setEditable : function(value){
16006         if(value == this.editable){
16007             return;
16008         }
16009         this.editable = value;
16010         if(!value){
16011             this.inputEl().dom.setAttribute('readOnly', true);
16012             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16013             this.inputEl().addClass('x-combo-noedit');
16014         }else{
16015             this.inputEl().dom.setAttribute('readOnly', false);
16016             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16017             this.inputEl().removeClass('x-combo-noedit');
16018         }
16019     },
16020
16021     // private
16022     
16023     onBeforeLoad : function(combo,opts){
16024         if(!this.hasFocus){
16025             return;
16026         }
16027          if (!opts.add) {
16028             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16029          }
16030         this.restrictHeight();
16031         this.selectedIndex = -1;
16032     },
16033
16034     // private
16035     onLoad : function(){
16036         
16037         this.hasQuery = false;
16038         
16039         if(!this.hasFocus){
16040             return;
16041         }
16042         
16043         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16044             this.loading.hide();
16045         }
16046         
16047         if(this.store.getCount() > 0){
16048             
16049             this.expand();
16050             this.restrictHeight();
16051             if(this.lastQuery == this.allQuery){
16052                 if(this.editable && !this.tickable){
16053                     this.inputEl().dom.select();
16054                 }
16055                 
16056                 if(
16057                     !this.selectByValue(this.value, true) &&
16058                     this.autoFocus && 
16059                     (
16060                         !this.store.lastOptions ||
16061                         typeof(this.store.lastOptions.add) == 'undefined' || 
16062                         this.store.lastOptions.add != true
16063                     )
16064                 ){
16065                     this.select(0, true);
16066                 }
16067             }else{
16068                 if(this.autoFocus){
16069                     this.selectNext();
16070                 }
16071                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16072                     this.taTask.delay(this.typeAheadDelay);
16073                 }
16074             }
16075         }else{
16076             this.onEmptyResults();
16077         }
16078         
16079         //this.el.focus();
16080     },
16081     // private
16082     onLoadException : function()
16083     {
16084         this.hasQuery = false;
16085         
16086         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16087             this.loading.hide();
16088         }
16089         
16090         if(this.tickable && this.editable){
16091             return;
16092         }
16093         
16094         this.collapse();
16095         // only causes errors at present
16096         //Roo.log(this.store.reader.jsonData);
16097         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16098             // fixme
16099             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16100         //}
16101         
16102         
16103     },
16104     // private
16105     onTypeAhead : function(){
16106         if(this.store.getCount() > 0){
16107             var r = this.store.getAt(0);
16108             var newValue = r.data[this.displayField];
16109             var len = newValue.length;
16110             var selStart = this.getRawValue().length;
16111             
16112             if(selStart != len){
16113                 this.setRawValue(newValue);
16114                 this.selectText(selStart, newValue.length);
16115             }
16116         }
16117     },
16118
16119     // private
16120     onSelect : function(record, index){
16121         
16122         if(this.fireEvent('beforeselect', this, record, index) !== false){
16123         
16124             this.setFromData(index > -1 ? record.data : false);
16125             
16126             this.collapse();
16127             this.fireEvent('select', this, record, index);
16128         }
16129     },
16130
16131     /**
16132      * Returns the currently selected field value or empty string if no value is set.
16133      * @return {String} value The selected value
16134      */
16135     getValue : function()
16136     {
16137         if(Roo.isIOS && this.useNativeIOS){
16138             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16139         }
16140         
16141         if(this.multiple){
16142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16143         }
16144         
16145         if(this.valueField){
16146             return typeof this.value != 'undefined' ? this.value : '';
16147         }else{
16148             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16149         }
16150     },
16151     
16152     getRawValue : function()
16153     {
16154         if(Roo.isIOS && this.useNativeIOS){
16155             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16156         }
16157         
16158         var v = this.inputEl().getValue();
16159         
16160         return v;
16161     },
16162
16163     /**
16164      * Clears any text/value currently set in the field
16165      */
16166     clearValue : function(){
16167         
16168         if(this.hiddenField){
16169             this.hiddenField.dom.value = '';
16170         }
16171         this.value = '';
16172         this.setRawValue('');
16173         this.lastSelectionText = '';
16174         this.lastData = false;
16175         
16176         var close = this.closeTriggerEl();
16177         
16178         if(close){
16179             close.hide();
16180         }
16181         
16182         this.validate();
16183         
16184     },
16185
16186     /**
16187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16188      * will be displayed in the field.  If the value does not match the data value of an existing item,
16189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16190      * Otherwise the field will be blank (although the value will still be set).
16191      * @param {String} value The value to match
16192      */
16193     setValue : function(v)
16194     {
16195         if(Roo.isIOS && this.useNativeIOS){
16196             this.setIOSValue(v);
16197             return;
16198         }
16199         
16200         if(this.multiple){
16201             this.syncValue();
16202             return;
16203         }
16204         
16205         var text = v;
16206         if(this.valueField){
16207             var r = this.findRecord(this.valueField, v);
16208             if(r){
16209                 text = r.data[this.displayField];
16210             }else if(this.valueNotFoundText !== undefined){
16211                 text = this.valueNotFoundText;
16212             }
16213         }
16214         this.lastSelectionText = text;
16215         if(this.hiddenField){
16216             this.hiddenField.dom.value = v;
16217         }
16218         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16219         this.value = v;
16220         
16221         var close = this.closeTriggerEl();
16222         
16223         if(close){
16224             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16225         }
16226         
16227         this.validate();
16228     },
16229     /**
16230      * @property {Object} the last set data for the element
16231      */
16232     
16233     lastData : false,
16234     /**
16235      * Sets the value of the field based on a object which is related to the record format for the store.
16236      * @param {Object} value the value to set as. or false on reset?
16237      */
16238     setFromData : function(o){
16239         
16240         if(this.multiple){
16241             this.addItem(o);
16242             return;
16243         }
16244             
16245         var dv = ''; // display value
16246         var vv = ''; // value value..
16247         this.lastData = o;
16248         if (this.displayField) {
16249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16250         } else {
16251             // this is an error condition!!!
16252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16253         }
16254         
16255         if(this.valueField){
16256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16257         }
16258         
16259         var close = this.closeTriggerEl();
16260         
16261         if(close){
16262             if(dv.length || vv * 1 > 0){
16263                 close.show() ;
16264                 this.blockFocus=true;
16265             } else {
16266                 close.hide();
16267             }             
16268         }
16269         
16270         if(this.hiddenField){
16271             this.hiddenField.dom.value = vv;
16272             
16273             this.lastSelectionText = dv;
16274             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16275             this.value = vv;
16276             return;
16277         }
16278         // no hidden field.. - we store the value in 'value', but still display
16279         // display field!!!!
16280         this.lastSelectionText = dv;
16281         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16282         this.value = vv;
16283         
16284         
16285         
16286     },
16287     // private
16288     reset : function(){
16289         // overridden so that last data is reset..
16290         
16291         if(this.multiple){
16292             this.clearItem();
16293             return;
16294         }
16295         
16296         this.setValue(this.originalValue);
16297         //this.clearInvalid();
16298         this.lastData = false;
16299         if (this.view) {
16300             this.view.clearSelections();
16301         }
16302         
16303         this.validate();
16304     },
16305     // private
16306     findRecord : function(prop, value){
16307         var record;
16308         if(this.store.getCount() > 0){
16309             this.store.each(function(r){
16310                 if(r.data[prop] == value){
16311                     record = r;
16312                     return false;
16313                 }
16314                 return true;
16315             });
16316         }
16317         return record;
16318     },
16319     
16320     getName: function()
16321     {
16322         // returns hidden if it's set..
16323         if (!this.rendered) {return ''};
16324         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16325         
16326     },
16327     // private
16328     onViewMove : function(e, t){
16329         this.inKeyMode = false;
16330     },
16331
16332     // private
16333     onViewOver : function(e, t){
16334         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16335             return;
16336         }
16337         var item = this.view.findItemFromChild(t);
16338         
16339         if(item){
16340             var index = this.view.indexOf(item);
16341             this.select(index, false);
16342         }
16343     },
16344
16345     // private
16346     onViewClick : function(view, doFocus, el, e)
16347     {
16348         var index = this.view.getSelectedIndexes()[0];
16349         
16350         var r = this.store.getAt(index);
16351         
16352         if(this.tickable){
16353             
16354             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16355                 return;
16356             }
16357             
16358             var rm = false;
16359             var _this = this;
16360             
16361             Roo.each(this.tickItems, function(v,k){
16362                 
16363                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16364                     Roo.log(v);
16365                     _this.tickItems.splice(k, 1);
16366                     
16367                     if(typeof(e) == 'undefined' && view == false){
16368                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16369                     }
16370                     
16371                     rm = true;
16372                     return;
16373                 }
16374             });
16375             
16376             if(rm){
16377                 return;
16378             }
16379             
16380             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16381                 this.tickItems.push(r.data);
16382             }
16383             
16384             if(typeof(e) == 'undefined' && view == false){
16385                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16386             }
16387                     
16388             return;
16389         }
16390         
16391         if(r){
16392             this.onSelect(r, index);
16393         }
16394         if(doFocus !== false && !this.blockFocus){
16395             this.inputEl().focus();
16396         }
16397     },
16398
16399     // private
16400     restrictHeight : function(){
16401         //this.innerList.dom.style.height = '';
16402         //var inner = this.innerList.dom;
16403         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16404         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16405         //this.list.beginUpdate();
16406         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16407         this.list.alignTo(this.inputEl(), this.listAlign);
16408         this.list.alignTo(this.inputEl(), this.listAlign);
16409         //this.list.endUpdate();
16410     },
16411
16412     // private
16413     onEmptyResults : function(){
16414         
16415         if(this.tickable && this.editable){
16416             this.hasFocus = false;
16417             this.restrictHeight();
16418             return;
16419         }
16420         
16421         this.collapse();
16422     },
16423
16424     /**
16425      * Returns true if the dropdown list is expanded, else false.
16426      */
16427     isExpanded : function(){
16428         return this.list.isVisible();
16429     },
16430
16431     /**
16432      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16434      * @param {String} value The data value of the item to select
16435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16436      * selected item if it is not currently in view (defaults to true)
16437      * @return {Boolean} True if the value matched an item in the list, else false
16438      */
16439     selectByValue : function(v, scrollIntoView){
16440         if(v !== undefined && v !== null){
16441             var r = this.findRecord(this.valueField || this.displayField, v);
16442             if(r){
16443                 this.select(this.store.indexOf(r), scrollIntoView);
16444                 return true;
16445             }
16446         }
16447         return false;
16448     },
16449
16450     /**
16451      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16453      * @param {Number} index The zero-based index of the list item to select
16454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16455      * selected item if it is not currently in view (defaults to true)
16456      */
16457     select : function(index, scrollIntoView){
16458         this.selectedIndex = index;
16459         this.view.select(index);
16460         if(scrollIntoView !== false){
16461             var el = this.view.getNode(index);
16462             /*
16463              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16464              */
16465             if(el){
16466                 this.list.scrollChildIntoView(el, false);
16467             }
16468         }
16469     },
16470
16471     // private
16472     selectNext : function(){
16473         var ct = this.store.getCount();
16474         if(ct > 0){
16475             if(this.selectedIndex == -1){
16476                 this.select(0);
16477             }else if(this.selectedIndex < ct-1){
16478                 this.select(this.selectedIndex+1);
16479             }
16480         }
16481     },
16482
16483     // private
16484     selectPrev : function(){
16485         var ct = this.store.getCount();
16486         if(ct > 0){
16487             if(this.selectedIndex == -1){
16488                 this.select(0);
16489             }else if(this.selectedIndex != 0){
16490                 this.select(this.selectedIndex-1);
16491             }
16492         }
16493     },
16494
16495     // private
16496     onKeyUp : function(e){
16497         if(this.editable !== false && !e.isSpecialKey()){
16498             this.lastKey = e.getKey();
16499             this.dqTask.delay(this.queryDelay);
16500         }
16501     },
16502
16503     // private
16504     validateBlur : function(){
16505         return !this.list || !this.list.isVisible();   
16506     },
16507
16508     // private
16509     initQuery : function(){
16510         
16511         var v = this.getRawValue();
16512         
16513         if(this.tickable && this.editable){
16514             v = this.tickableInputEl().getValue();
16515         }
16516         
16517         this.doQuery(v);
16518     },
16519
16520     // private
16521     doForce : function(){
16522         if(this.inputEl().dom.value.length > 0){
16523             this.inputEl().dom.value =
16524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16525              
16526         }
16527     },
16528
16529     /**
16530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16531      * query allowing the query action to be canceled if needed.
16532      * @param {String} query The SQL query to execute
16533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16535      * saved in the current store (defaults to false)
16536      */
16537     doQuery : function(q, forceAll){
16538         
16539         if(q === undefined || q === null){
16540             q = '';
16541         }
16542         var qe = {
16543             query: q,
16544             forceAll: forceAll,
16545             combo: this,
16546             cancel:false
16547         };
16548         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16549             return false;
16550         }
16551         q = qe.query;
16552         
16553         forceAll = qe.forceAll;
16554         if(forceAll === true || (q.length >= this.minChars)){
16555             
16556             this.hasQuery = true;
16557             
16558             if(this.lastQuery != q || this.alwaysQuery){
16559                 this.lastQuery = q;
16560                 if(this.mode == 'local'){
16561                     this.selectedIndex = -1;
16562                     if(forceAll){
16563                         this.store.clearFilter();
16564                     }else{
16565                         
16566                         if(this.specialFilter){
16567                             this.fireEvent('specialfilter', this);
16568                             this.onLoad();
16569                             return;
16570                         }
16571                         
16572                         this.store.filter(this.displayField, q);
16573                     }
16574                     
16575                     this.store.fireEvent("datachanged", this.store);
16576                     
16577                     this.onLoad();
16578                     
16579                     
16580                 }else{
16581                     
16582                     this.store.baseParams[this.queryParam] = q;
16583                     
16584                     var options = {params : this.getParams(q)};
16585                     
16586                     if(this.loadNext){
16587                         options.add = true;
16588                         options.params.start = this.page * this.pageSize;
16589                     }
16590                     
16591                     this.store.load(options);
16592                     
16593                     /*
16594                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16595                      *  we should expand the list on onLoad
16596                      *  so command out it
16597                      */
16598 //                    this.expand();
16599                 }
16600             }else{
16601                 this.selectedIndex = -1;
16602                 this.onLoad();   
16603             }
16604         }
16605         
16606         this.loadNext = false;
16607     },
16608     
16609     // private
16610     getParams : function(q){
16611         var p = {};
16612         //p[this.queryParam] = q;
16613         
16614         if(this.pageSize){
16615             p.start = 0;
16616             p.limit = this.pageSize;
16617         }
16618         return p;
16619     },
16620
16621     /**
16622      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16623      */
16624     collapse : function(){
16625         if(!this.isExpanded()){
16626             return;
16627         }
16628         
16629         this.list.hide();
16630         
16631         this.hasFocus = false;
16632         
16633         if(this.tickable){
16634             this.okBtn.hide();
16635             this.cancelBtn.hide();
16636             this.trigger.show();
16637             
16638             if(this.editable){
16639                 this.tickableInputEl().dom.value = '';
16640                 this.tickableInputEl().blur();
16641             }
16642             
16643         }
16644         
16645         Roo.get(document).un('mousedown', this.collapseIf, this);
16646         Roo.get(document).un('mousewheel', this.collapseIf, this);
16647         if (!this.editable) {
16648             Roo.get(document).un('keydown', this.listKeyPress, this);
16649         }
16650         this.fireEvent('collapse', this);
16651         
16652         this.validate();
16653     },
16654
16655     // private
16656     collapseIf : function(e){
16657         var in_combo  = e.within(this.el);
16658         var in_list =  e.within(this.list);
16659         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16660         
16661         if (in_combo || in_list || is_list) {
16662             //e.stopPropagation();
16663             return;
16664         }
16665         
16666         if(this.tickable){
16667             this.onTickableFooterButtonClick(e, false, false);
16668         }
16669
16670         this.collapse();
16671         
16672     },
16673
16674     /**
16675      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16676      */
16677     expand : function(){
16678        
16679         if(this.isExpanded() || !this.hasFocus){
16680             return;
16681         }
16682         
16683         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16684         this.list.setWidth(lw);
16685         
16686         Roo.log('expand');
16687         
16688         this.list.show();
16689         
16690         this.restrictHeight();
16691         
16692         if(this.tickable){
16693             
16694             this.tickItems = Roo.apply([], this.item);
16695             
16696             this.okBtn.show();
16697             this.cancelBtn.show();
16698             this.trigger.hide();
16699             
16700             if(this.editable){
16701                 this.tickableInputEl().focus();
16702             }
16703             
16704         }
16705         
16706         Roo.get(document).on('mousedown', this.collapseIf, this);
16707         Roo.get(document).on('mousewheel', this.collapseIf, this);
16708         if (!this.editable) {
16709             Roo.get(document).on('keydown', this.listKeyPress, this);
16710         }
16711         
16712         this.fireEvent('expand', this);
16713     },
16714
16715     // private
16716     // Implements the default empty TriggerField.onTriggerClick function
16717     onTriggerClick : function(e)
16718     {
16719         Roo.log('trigger click');
16720         
16721         if(this.disabled || !this.triggerList){
16722             return;
16723         }
16724         
16725         this.page = 0;
16726         this.loadNext = false;
16727         
16728         if(this.isExpanded()){
16729             this.collapse();
16730             if (!this.blockFocus) {
16731                 this.inputEl().focus();
16732             }
16733             
16734         }else {
16735             this.hasFocus = true;
16736             if(this.triggerAction == 'all') {
16737                 this.doQuery(this.allQuery, true);
16738             } else {
16739                 this.doQuery(this.getRawValue());
16740             }
16741             if (!this.blockFocus) {
16742                 this.inputEl().focus();
16743             }
16744         }
16745     },
16746     
16747     onTickableTriggerClick : function(e)
16748     {
16749         if(this.disabled){
16750             return;
16751         }
16752         
16753         this.page = 0;
16754         this.loadNext = false;
16755         this.hasFocus = true;
16756         
16757         if(this.triggerAction == 'all') {
16758             this.doQuery(this.allQuery, true);
16759         } else {
16760             this.doQuery(this.getRawValue());
16761         }
16762     },
16763     
16764     onSearchFieldClick : function(e)
16765     {
16766         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16767             this.onTickableFooterButtonClick(e, false, false);
16768             return;
16769         }
16770         
16771         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16772             return;
16773         }
16774         
16775         this.page = 0;
16776         this.loadNext = false;
16777         this.hasFocus = true;
16778         
16779         if(this.triggerAction == 'all') {
16780             this.doQuery(this.allQuery, true);
16781         } else {
16782             this.doQuery(this.getRawValue());
16783         }
16784     },
16785     
16786     listKeyPress : function(e)
16787     {
16788         //Roo.log('listkeypress');
16789         // scroll to first matching element based on key pres..
16790         if (e.isSpecialKey()) {
16791             return false;
16792         }
16793         var k = String.fromCharCode(e.getKey()).toUpperCase();
16794         //Roo.log(k);
16795         var match  = false;
16796         var csel = this.view.getSelectedNodes();
16797         var cselitem = false;
16798         if (csel.length) {
16799             var ix = this.view.indexOf(csel[0]);
16800             cselitem  = this.store.getAt(ix);
16801             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16802                 cselitem = false;
16803             }
16804             
16805         }
16806         
16807         this.store.each(function(v) { 
16808             if (cselitem) {
16809                 // start at existing selection.
16810                 if (cselitem.id == v.id) {
16811                     cselitem = false;
16812                 }
16813                 return true;
16814             }
16815                 
16816             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16817                 match = this.store.indexOf(v);
16818                 return false;
16819             }
16820             return true;
16821         }, this);
16822         
16823         if (match === false) {
16824             return true; // no more action?
16825         }
16826         // scroll to?
16827         this.view.select(match);
16828         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16829         sn.scrollIntoView(sn.dom.parentNode, false);
16830     },
16831     
16832     onViewScroll : function(e, t){
16833         
16834         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
16835             return;
16836         }
16837         
16838         this.hasQuery = true;
16839         
16840         this.loading = this.list.select('.loading', true).first();
16841         
16842         if(this.loading === null){
16843             this.list.createChild({
16844                 tag: 'div',
16845                 cls: 'loading roo-select2-more-results roo-select2-active',
16846                 html: 'Loading more results...'
16847             });
16848             
16849             this.loading = this.list.select('.loading', true).first();
16850             
16851             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16852             
16853             this.loading.hide();
16854         }
16855         
16856         this.loading.show();
16857         
16858         var _combo = this;
16859         
16860         this.page++;
16861         this.loadNext = true;
16862         
16863         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16864         
16865         return;
16866     },
16867     
16868     addItem : function(o)
16869     {   
16870         var dv = ''; // display value
16871         
16872         if (this.displayField) {
16873             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16874         } else {
16875             // this is an error condition!!!
16876             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16877         }
16878         
16879         if(!dv.length){
16880             return;
16881         }
16882         
16883         var choice = this.choices.createChild({
16884             tag: 'li',
16885             cls: 'roo-select2-search-choice',
16886             cn: [
16887                 {
16888                     tag: 'div',
16889                     html: dv
16890                 },
16891                 {
16892                     tag: 'a',
16893                     href: '#',
16894                     cls: 'roo-select2-search-choice-close fa fa-times',
16895                     tabindex: '-1'
16896                 }
16897             ]
16898             
16899         }, this.searchField);
16900         
16901         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16902         
16903         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16904         
16905         this.item.push(o);
16906         
16907         this.lastData = o;
16908         
16909         this.syncValue();
16910         
16911         this.inputEl().dom.value = '';
16912         
16913         this.validate();
16914     },
16915     
16916     onRemoveItem : function(e, _self, o)
16917     {
16918         e.preventDefault();
16919         
16920         this.lastItem = Roo.apply([], this.item);
16921         
16922         var index = this.item.indexOf(o.data) * 1;
16923         
16924         if( index < 0){
16925             Roo.log('not this item?!');
16926             return;
16927         }
16928         
16929         this.item.splice(index, 1);
16930         o.item.remove();
16931         
16932         this.syncValue();
16933         
16934         this.fireEvent('remove', this, e);
16935         
16936         this.validate();
16937         
16938     },
16939     
16940     syncValue : function()
16941     {
16942         if(!this.item.length){
16943             this.clearValue();
16944             return;
16945         }
16946             
16947         var value = [];
16948         var _this = this;
16949         Roo.each(this.item, function(i){
16950             if(_this.valueField){
16951                 value.push(i[_this.valueField]);
16952                 return;
16953             }
16954
16955             value.push(i);
16956         });
16957
16958         this.value = value.join(',');
16959
16960         if(this.hiddenField){
16961             this.hiddenField.dom.value = this.value;
16962         }
16963         
16964         this.store.fireEvent("datachanged", this.store);
16965         
16966         this.validate();
16967     },
16968     
16969     clearItem : function()
16970     {
16971         if(!this.multiple){
16972             return;
16973         }
16974         
16975         this.item = [];
16976         
16977         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16978            c.remove();
16979         });
16980         
16981         this.syncValue();
16982         
16983         this.validate();
16984         
16985         if(this.tickable && !Roo.isTouch){
16986             this.view.refresh();
16987         }
16988     },
16989     
16990     inputEl: function ()
16991     {
16992         if(Roo.isIOS && this.useNativeIOS){
16993             return this.el.select('select.roo-ios-select', true).first();
16994         }
16995         
16996         if(Roo.isTouch && this.mobileTouchView){
16997             return this.el.select('input.form-control',true).first();
16998         }
16999         
17000         if(this.tickable){
17001             return this.searchField;
17002         }
17003         
17004         return this.el.select('input.form-control',true).first();
17005     },
17006     
17007     onTickableFooterButtonClick : function(e, btn, el)
17008     {
17009         e.preventDefault();
17010         
17011         this.lastItem = Roo.apply([], this.item);
17012         
17013         if(btn && btn.name == 'cancel'){
17014             this.tickItems = Roo.apply([], this.item);
17015             this.collapse();
17016             return;
17017         }
17018         
17019         this.clearItem();
17020         
17021         var _this = this;
17022         
17023         Roo.each(this.tickItems, function(o){
17024             _this.addItem(o);
17025         });
17026         
17027         this.collapse();
17028         
17029     },
17030     
17031     validate : function()
17032     {
17033         if(this.getVisibilityEl().hasClass('hidden')){
17034             return true;
17035         }
17036         
17037         var v = this.getRawValue();
17038         
17039         if(this.multiple){
17040             v = this.getValue();
17041         }
17042         
17043         if(this.disabled || this.allowBlank || v.length){
17044             this.markValid();
17045             return true;
17046         }
17047         
17048         this.markInvalid();
17049         return false;
17050     },
17051     
17052     tickableInputEl : function()
17053     {
17054         if(!this.tickable || !this.editable){
17055             return this.inputEl();
17056         }
17057         
17058         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17059     },
17060     
17061     
17062     getAutoCreateTouchView : function()
17063     {
17064         var id = Roo.id();
17065         
17066         var cfg = {
17067             cls: 'form-group' //input-group
17068         };
17069         
17070         var input =  {
17071             tag: 'input',
17072             id : id,
17073             type : this.inputType,
17074             cls : 'form-control x-combo-noedit',
17075             autocomplete: 'new-password',
17076             placeholder : this.placeholder || '',
17077             readonly : true
17078         };
17079         
17080         if (this.name) {
17081             input.name = this.name;
17082         }
17083         
17084         if (this.size) {
17085             input.cls += ' input-' + this.size;
17086         }
17087         
17088         if (this.disabled) {
17089             input.disabled = true;
17090         }
17091         
17092         var inputblock = {
17093             cls : 'roo-combobox-wrap',
17094             cn : [
17095                 input
17096             ]
17097         };
17098         
17099         if(this.before){
17100             inputblock.cls += ' input-group';
17101             
17102             inputblock.cn.unshift({
17103                 tag :'span',
17104                 cls : 'input-group-addon input-group-prepend input-group-text',
17105                 html : this.before
17106             });
17107         }
17108         
17109         if(this.removable && !this.multiple){
17110             inputblock.cls += ' roo-removable';
17111             
17112             inputblock.cn.push({
17113                 tag: 'button',
17114                 html : 'x',
17115                 cls : 'roo-combo-removable-btn close'
17116             });
17117         }
17118
17119         if(this.hasFeedback && !this.allowBlank){
17120             
17121             inputblock.cls += ' has-feedback';
17122             
17123             inputblock.cn.push({
17124                 tag: 'span',
17125                 cls: 'glyphicon form-control-feedback'
17126             });
17127             
17128         }
17129         
17130         if (this.after) {
17131             
17132             inputblock.cls += (this.before) ? '' : ' input-group';
17133             
17134             inputblock.cn.push({
17135                 tag :'span',
17136                 cls : 'input-group-addon input-group-append input-group-text',
17137                 html : this.after
17138             });
17139         }
17140
17141         
17142         var ibwrap = inputblock;
17143         
17144         if(this.multiple){
17145             ibwrap = {
17146                 tag: 'ul',
17147                 cls: 'roo-select2-choices',
17148                 cn:[
17149                     {
17150                         tag: 'li',
17151                         cls: 'roo-select2-search-field',
17152                         cn: [
17153
17154                             inputblock
17155                         ]
17156                     }
17157                 ]
17158             };
17159         
17160             
17161         }
17162         
17163         var combobox = {
17164             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17165             cn: [
17166                 {
17167                     tag: 'input',
17168                     type : 'hidden',
17169                     cls: 'form-hidden-field'
17170                 },
17171                 ibwrap
17172             ]
17173         };
17174         
17175         if(!this.multiple && this.showToggleBtn){
17176             
17177             var caret = {
17178                 cls: 'caret'
17179             };
17180             
17181             if (this.caret != false) {
17182                 caret = {
17183                      tag: 'i',
17184                      cls: 'fa fa-' + this.caret
17185                 };
17186                 
17187             }
17188             
17189             combobox.cn.push({
17190                 tag :'span',
17191                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17192                 cn : [
17193                     Roo.bootstrap.version == 3 ? caret : '',
17194                     {
17195                         tag: 'span',
17196                         cls: 'combobox-clear',
17197                         cn  : [
17198                             {
17199                                 tag : 'i',
17200                                 cls: 'icon-remove'
17201                             }
17202                         ]
17203                     }
17204                 ]
17205
17206             })
17207         }
17208         
17209         if(this.multiple){
17210             combobox.cls += ' roo-select2-container-multi';
17211         }
17212         
17213         var align = this.labelAlign || this.parentLabelAlign();
17214         
17215         if (align ==='left' && this.fieldLabel.length) {
17216
17217             cfg.cn = [
17218                 {
17219                    tag : 'i',
17220                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17221                    tooltip : 'This field is required'
17222                 },
17223                 {
17224                     tag: 'label',
17225                     cls : 'control-label col-form-label',
17226                     html : this.fieldLabel
17227
17228                 },
17229                 {
17230                     cls : 'roo-combobox-wrap ', 
17231                     cn: [
17232                         combobox
17233                     ]
17234                 }
17235             ];
17236             
17237             var labelCfg = cfg.cn[1];
17238             var contentCfg = cfg.cn[2];
17239             
17240
17241             if(this.indicatorpos == 'right'){
17242                 cfg.cn = [
17243                     {
17244                         tag: 'label',
17245                         'for' :  id,
17246                         cls : 'control-label col-form-label',
17247                         cn : [
17248                             {
17249                                 tag : 'span',
17250                                 html : this.fieldLabel
17251                             },
17252                             {
17253                                 tag : 'i',
17254                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17255                                 tooltip : 'This field is required'
17256                             }
17257                         ]
17258                     },
17259                     {
17260                         cls : "roo-combobox-wrap ",
17261                         cn: [
17262                             combobox
17263                         ]
17264                     }
17265
17266                 ];
17267                 
17268                 labelCfg = cfg.cn[0];
17269                 contentCfg = cfg.cn[1];
17270             }
17271             
17272            
17273             
17274             if(this.labelWidth > 12){
17275                 labelCfg.style = "width: " + this.labelWidth + 'px';
17276             }
17277            
17278             if(this.labelWidth < 13 && this.labelmd == 0){
17279                 this.labelmd = this.labelWidth;
17280             }
17281             
17282             if(this.labellg > 0){
17283                 labelCfg.cls += ' col-lg-' + this.labellg;
17284                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17285             }
17286             
17287             if(this.labelmd > 0){
17288                 labelCfg.cls += ' col-md-' + this.labelmd;
17289                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17290             }
17291             
17292             if(this.labelsm > 0){
17293                 labelCfg.cls += ' col-sm-' + this.labelsm;
17294                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17295             }
17296             
17297             if(this.labelxs > 0){
17298                 labelCfg.cls += ' col-xs-' + this.labelxs;
17299                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17300             }
17301                 
17302                 
17303         } else if ( this.fieldLabel.length) {
17304             cfg.cn = [
17305                 {
17306                    tag : 'i',
17307                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17308                    tooltip : 'This field is required'
17309                 },
17310                 {
17311                     tag: 'label',
17312                     cls : 'control-label',
17313                     html : this.fieldLabel
17314
17315                 },
17316                 {
17317                     cls : '', 
17318                     cn: [
17319                         combobox
17320                     ]
17321                 }
17322             ];
17323             
17324             if(this.indicatorpos == 'right'){
17325                 cfg.cn = [
17326                     {
17327                         tag: 'label',
17328                         cls : 'control-label',
17329                         html : this.fieldLabel,
17330                         cn : [
17331                             {
17332                                tag : 'i',
17333                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17334                                tooltip : 'This field is required'
17335                             }
17336                         ]
17337                     },
17338                     {
17339                         cls : '', 
17340                         cn: [
17341                             combobox
17342                         ]
17343                     }
17344                 ];
17345             }
17346         } else {
17347             cfg.cn = combobox;    
17348         }
17349         
17350         
17351         var settings = this;
17352         
17353         ['xs','sm','md','lg'].map(function(size){
17354             if (settings[size]) {
17355                 cfg.cls += ' col-' + size + '-' + settings[size];
17356             }
17357         });
17358         
17359         return cfg;
17360     },
17361     
17362     initTouchView : function()
17363     {
17364         this.renderTouchView();
17365         
17366         this.touchViewEl.on('scroll', function(){
17367             this.el.dom.scrollTop = 0;
17368         }, this);
17369         
17370         this.originalValue = this.getValue();
17371         
17372         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17373         
17374         this.inputEl().on("click", this.showTouchView, this);
17375         if (this.triggerEl) {
17376             this.triggerEl.on("click", this.showTouchView, this);
17377         }
17378         
17379         
17380         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17381         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17382         
17383         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17384         
17385         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17386         this.store.on('load', this.onTouchViewLoad, this);
17387         this.store.on('loadexception', this.onTouchViewLoadException, this);
17388         
17389         if(this.hiddenName){
17390             
17391             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17392             
17393             this.hiddenField.dom.value =
17394                 this.hiddenValue !== undefined ? this.hiddenValue :
17395                 this.value !== undefined ? this.value : '';
17396         
17397             this.el.dom.removeAttribute('name');
17398             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17399         }
17400         
17401         if(this.multiple){
17402             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17403             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17404         }
17405         
17406         if(this.removable && !this.multiple){
17407             var close = this.closeTriggerEl();
17408             if(close){
17409                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17410                 close.on('click', this.removeBtnClick, this, close);
17411             }
17412         }
17413         /*
17414          * fix the bug in Safari iOS8
17415          */
17416         this.inputEl().on("focus", function(e){
17417             document.activeElement.blur();
17418         }, this);
17419         
17420         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17421         
17422         return;
17423         
17424         
17425     },
17426     
17427     renderTouchView : function()
17428     {
17429         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17430         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17431         
17432         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17433         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434         
17435         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17436         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437         this.touchViewBodyEl.setStyle('overflow', 'auto');
17438         
17439         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17440         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17441         
17442         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17443         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444         
17445     },
17446     
17447     showTouchView : function()
17448     {
17449         if(this.disabled){
17450             return;
17451         }
17452         
17453         this.touchViewHeaderEl.hide();
17454
17455         if(this.modalTitle.length){
17456             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17457             this.touchViewHeaderEl.show();
17458         }
17459
17460         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17461         this.touchViewEl.show();
17462
17463         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17464         
17465         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17466         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17467
17468         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17469
17470         if(this.modalTitle.length){
17471             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17472         }
17473         
17474         this.touchViewBodyEl.setHeight(bodyHeight);
17475
17476         if(this.animate){
17477             var _this = this;
17478             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17479         }else{
17480             this.touchViewEl.addClass(['in','show']);
17481         }
17482         
17483         if(this._touchViewMask){
17484             Roo.get(document.body).addClass("x-body-masked");
17485             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17486             this._touchViewMask.setStyle('z-index', 10000);
17487             this._touchViewMask.addClass('show');
17488         }
17489         
17490         this.doTouchViewQuery();
17491         
17492     },
17493     
17494     hideTouchView : function()
17495     {
17496         this.touchViewEl.removeClass(['in','show']);
17497
17498         if(this.animate){
17499             var _this = this;
17500             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17501         }else{
17502             this.touchViewEl.setStyle('display', 'none');
17503         }
17504         
17505         if(this._touchViewMask){
17506             this._touchViewMask.removeClass('show');
17507             Roo.get(document.body).removeClass("x-body-masked");
17508         }
17509     },
17510     
17511     setTouchViewValue : function()
17512     {
17513         if(this.multiple){
17514             this.clearItem();
17515         
17516             var _this = this;
17517
17518             Roo.each(this.tickItems, function(o){
17519                 this.addItem(o);
17520             }, this);
17521         }
17522         
17523         this.hideTouchView();
17524     },
17525     
17526     doTouchViewQuery : function()
17527     {
17528         var qe = {
17529             query: '',
17530             forceAll: true,
17531             combo: this,
17532             cancel:false
17533         };
17534         
17535         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17536             return false;
17537         }
17538         
17539         if(!this.alwaysQuery || this.mode == 'local'){
17540             this.onTouchViewLoad();
17541             return;
17542         }
17543         
17544         this.store.load();
17545     },
17546     
17547     onTouchViewBeforeLoad : function(combo,opts)
17548     {
17549         return;
17550     },
17551
17552     // private
17553     onTouchViewLoad : function()
17554     {
17555         if(this.store.getCount() < 1){
17556             this.onTouchViewEmptyResults();
17557             return;
17558         }
17559         
17560         this.clearTouchView();
17561         
17562         var rawValue = this.getRawValue();
17563         
17564         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17565         
17566         this.tickItems = [];
17567         
17568         this.store.data.each(function(d, rowIndex){
17569             var row = this.touchViewListGroup.createChild(template);
17570             
17571             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17572                 row.addClass(d.data.cls);
17573             }
17574             
17575             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17576                 var cfg = {
17577                     data : d.data,
17578                     html : d.data[this.displayField]
17579                 };
17580                 
17581                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17582                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17583                 }
17584             }
17585             row.removeClass('selected');
17586             if(!this.multiple && this.valueField &&
17587                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17588             {
17589                 // radio buttons..
17590                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17591                 row.addClass('selected');
17592             }
17593             
17594             if(this.multiple && this.valueField &&
17595                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17596             {
17597                 
17598                 // checkboxes...
17599                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17600                 this.tickItems.push(d.data);
17601             }
17602             
17603             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17604             
17605         }, this);
17606         
17607         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17608         
17609         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17610
17611         if(this.modalTitle.length){
17612             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17613         }
17614
17615         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17616         
17617         if(this.mobile_restrict_height && listHeight < bodyHeight){
17618             this.touchViewBodyEl.setHeight(listHeight);
17619         }
17620         
17621         var _this = this;
17622         
17623         if(firstChecked && listHeight > bodyHeight){
17624             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17625         }
17626         
17627     },
17628     
17629     onTouchViewLoadException : function()
17630     {
17631         this.hideTouchView();
17632     },
17633     
17634     onTouchViewEmptyResults : function()
17635     {
17636         this.clearTouchView();
17637         
17638         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17639         
17640         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17641         
17642     },
17643     
17644     clearTouchView : function()
17645     {
17646         this.touchViewListGroup.dom.innerHTML = '';
17647     },
17648     
17649     onTouchViewClick : function(e, el, o)
17650     {
17651         e.preventDefault();
17652         
17653         var row = o.row;
17654         var rowIndex = o.rowIndex;
17655         
17656         var r = this.store.getAt(rowIndex);
17657         
17658         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17659             
17660             if(!this.multiple){
17661                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17662                     c.dom.removeAttribute('checked');
17663                 }, this);
17664
17665                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17666
17667                 this.setFromData(r.data);
17668
17669                 var close = this.closeTriggerEl();
17670
17671                 if(close){
17672                     close.show();
17673                 }
17674
17675                 this.hideTouchView();
17676
17677                 this.fireEvent('select', this, r, rowIndex);
17678
17679                 return;
17680             }
17681
17682             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17683                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17684                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17685                 return;
17686             }
17687
17688             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689             this.addItem(r.data);
17690             this.tickItems.push(r.data);
17691         }
17692     },
17693     
17694     getAutoCreateNativeIOS : function()
17695     {
17696         var cfg = {
17697             cls: 'form-group' //input-group,
17698         };
17699         
17700         var combobox =  {
17701             tag: 'select',
17702             cls : 'roo-ios-select'
17703         };
17704         
17705         if (this.name) {
17706             combobox.name = this.name;
17707         }
17708         
17709         if (this.disabled) {
17710             combobox.disabled = true;
17711         }
17712         
17713         var settings = this;
17714         
17715         ['xs','sm','md','lg'].map(function(size){
17716             if (settings[size]) {
17717                 cfg.cls += ' col-' + size + '-' + settings[size];
17718             }
17719         });
17720         
17721         cfg.cn = combobox;
17722         
17723         return cfg;
17724         
17725     },
17726     
17727     initIOSView : function()
17728     {
17729         this.store.on('load', this.onIOSViewLoad, this);
17730         
17731         return;
17732     },
17733     
17734     onIOSViewLoad : function()
17735     {
17736         if(this.store.getCount() < 1){
17737             return;
17738         }
17739         
17740         this.clearIOSView();
17741         
17742         if(this.allowBlank) {
17743             
17744             var default_text = '-- SELECT --';
17745             
17746             if(this.placeholder.length){
17747                 default_text = this.placeholder;
17748             }
17749             
17750             if(this.emptyTitle.length){
17751                 default_text += ' - ' + this.emptyTitle + ' -';
17752             }
17753             
17754             var opt = this.inputEl().createChild({
17755                 tag: 'option',
17756                 value : 0,
17757                 html : default_text
17758             });
17759             
17760             var o = {};
17761             o[this.valueField] = 0;
17762             o[this.displayField] = default_text;
17763             
17764             this.ios_options.push({
17765                 data : o,
17766                 el : opt
17767             });
17768             
17769         }
17770         
17771         this.store.data.each(function(d, rowIndex){
17772             
17773             var html = '';
17774             
17775             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17776                 html = d.data[this.displayField];
17777             }
17778             
17779             var value = '';
17780             
17781             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17782                 value = d.data[this.valueField];
17783             }
17784             
17785             var option = {
17786                 tag: 'option',
17787                 value : value,
17788                 html : html
17789             };
17790             
17791             if(this.value == d.data[this.valueField]){
17792                 option['selected'] = true;
17793             }
17794             
17795             var opt = this.inputEl().createChild(option);
17796             
17797             this.ios_options.push({
17798                 data : d.data,
17799                 el : opt
17800             });
17801             
17802         }, this);
17803         
17804         this.inputEl().on('change', function(){
17805            this.fireEvent('select', this);
17806         }, this);
17807         
17808     },
17809     
17810     clearIOSView: function()
17811     {
17812         this.inputEl().dom.innerHTML = '';
17813         
17814         this.ios_options = [];
17815     },
17816     
17817     setIOSValue: function(v)
17818     {
17819         this.value = v;
17820         
17821         if(!this.ios_options){
17822             return;
17823         }
17824         
17825         Roo.each(this.ios_options, function(opts){
17826            
17827            opts.el.dom.removeAttribute('selected');
17828            
17829            if(opts.data[this.valueField] != v){
17830                return;
17831            }
17832            
17833            opts.el.dom.setAttribute('selected', true);
17834            
17835         }, this);
17836     }
17837
17838     /** 
17839     * @cfg {Boolean} grow 
17840     * @hide 
17841     */
17842     /** 
17843     * @cfg {Number} growMin 
17844     * @hide 
17845     */
17846     /** 
17847     * @cfg {Number} growMax 
17848     * @hide 
17849     */
17850     /**
17851      * @hide
17852      * @method autoSize
17853      */
17854 });
17855
17856 Roo.apply(Roo.bootstrap.ComboBox,  {
17857     
17858     header : {
17859         tag: 'div',
17860         cls: 'modal-header',
17861         cn: [
17862             {
17863                 tag: 'h4',
17864                 cls: 'modal-title'
17865             }
17866         ]
17867     },
17868     
17869     body : {
17870         tag: 'div',
17871         cls: 'modal-body',
17872         cn: [
17873             {
17874                 tag: 'ul',
17875                 cls: 'list-group'
17876             }
17877         ]
17878     },
17879     
17880     listItemRadio : {
17881         tag: 'li',
17882         cls: 'list-group-item',
17883         cn: [
17884             {
17885                 tag: 'span',
17886                 cls: 'roo-combobox-list-group-item-value'
17887             },
17888             {
17889                 tag: 'div',
17890                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17891                 cn: [
17892                     {
17893                         tag: 'input',
17894                         type: 'radio'
17895                     },
17896                     {
17897                         tag: 'label'
17898                     }
17899                 ]
17900             }
17901         ]
17902     },
17903     
17904     listItemCheckbox : {
17905         tag: 'li',
17906         cls: 'list-group-item',
17907         cn: [
17908             {
17909                 tag: 'span',
17910                 cls: 'roo-combobox-list-group-item-value'
17911             },
17912             {
17913                 tag: 'div',
17914                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17915                 cn: [
17916                     {
17917                         tag: 'input',
17918                         type: 'checkbox'
17919                     },
17920                     {
17921                         tag: 'label'
17922                     }
17923                 ]
17924             }
17925         ]
17926     },
17927     
17928     emptyResult : {
17929         tag: 'div',
17930         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17931     },
17932     
17933     footer : {
17934         tag: 'div',
17935         cls: 'modal-footer',
17936         cn: [
17937             {
17938                 tag: 'div',
17939                 cls: 'row',
17940                 cn: [
17941                     {
17942                         tag: 'div',
17943                         cls: 'col-xs-6 text-left',
17944                         cn: {
17945                             tag: 'button',
17946                             cls: 'btn btn-danger roo-touch-view-cancel',
17947                             html: 'Cancel'
17948                         }
17949                     },
17950                     {
17951                         tag: 'div',
17952                         cls: 'col-xs-6 text-right',
17953                         cn: {
17954                             tag: 'button',
17955                             cls: 'btn btn-success roo-touch-view-ok',
17956                             html: 'OK'
17957                         }
17958                     }
17959                 ]
17960             }
17961         ]
17962         
17963     }
17964 });
17965
17966 Roo.apply(Roo.bootstrap.ComboBox,  {
17967     
17968     touchViewTemplate : {
17969         tag: 'div',
17970         cls: 'modal fade roo-combobox-touch-view',
17971         cn: [
17972             {
17973                 tag: 'div',
17974                 cls: 'modal-dialog',
17975                 style : 'position:fixed', // we have to fix position....
17976                 cn: [
17977                     {
17978                         tag: 'div',
17979                         cls: 'modal-content',
17980                         cn: [
17981                             Roo.bootstrap.ComboBox.header,
17982                             Roo.bootstrap.ComboBox.body,
17983                             Roo.bootstrap.ComboBox.footer
17984                         ]
17985                     }
17986                 ]
17987             }
17988         ]
17989     }
17990 });/*
17991  * Based on:
17992  * Ext JS Library 1.1.1
17993  * Copyright(c) 2006-2007, Ext JS, LLC.
17994  *
17995  * Originally Released Under LGPL - original licence link has changed is not relivant.
17996  *
17997  * Fork - LGPL
17998  * <script type="text/javascript">
17999  */
18000
18001 /**
18002  * @class Roo.View
18003  * @extends Roo.util.Observable
18004  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18005  * This class also supports single and multi selection modes. <br>
18006  * Create a data model bound view:
18007  <pre><code>
18008  var store = new Roo.data.Store(...);
18009
18010  var view = new Roo.View({
18011     el : "my-element",
18012     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18013  
18014     singleSelect: true,
18015     selectedClass: "ydataview-selected",
18016     store: store
18017  });
18018
18019  // listen for node click?
18020  view.on("click", function(vw, index, node, e){
18021  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18022  });
18023
18024  // load XML data
18025  dataModel.load("foobar.xml");
18026  </code></pre>
18027  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18028  * <br><br>
18029  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18030  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18031  * 
18032  * Note: old style constructor is still suported (container, template, config)
18033  * 
18034  * @constructor
18035  * Create a new View
18036  * @param {Object} config The config object
18037  * 
18038  */
18039 Roo.View = function(config, depreciated_tpl, depreciated_config){
18040     
18041     this.parent = false;
18042     
18043     if (typeof(depreciated_tpl) == 'undefined') {
18044         // new way.. - universal constructor.
18045         Roo.apply(this, config);
18046         this.el  = Roo.get(this.el);
18047     } else {
18048         // old format..
18049         this.el  = Roo.get(config);
18050         this.tpl = depreciated_tpl;
18051         Roo.apply(this, depreciated_config);
18052     }
18053     this.wrapEl  = this.el.wrap().wrap();
18054     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18055     
18056     
18057     if(typeof(this.tpl) == "string"){
18058         this.tpl = new Roo.Template(this.tpl);
18059     } else {
18060         // support xtype ctors..
18061         this.tpl = new Roo.factory(this.tpl, Roo);
18062     }
18063     
18064     
18065     this.tpl.compile();
18066     
18067     /** @private */
18068     this.addEvents({
18069         /**
18070          * @event beforeclick
18071          * Fires before a click is processed. Returns false to cancel the default action.
18072          * @param {Roo.View} this
18073          * @param {Number} index The index of the target node
18074          * @param {HTMLElement} node The target node
18075          * @param {Roo.EventObject} e The raw event object
18076          */
18077             "beforeclick" : true,
18078         /**
18079          * @event click
18080          * Fires when a template node is clicked.
18081          * @param {Roo.View} this
18082          * @param {Number} index The index of the target node
18083          * @param {HTMLElement} node The target node
18084          * @param {Roo.EventObject} e The raw event object
18085          */
18086             "click" : true,
18087         /**
18088          * @event dblclick
18089          * Fires when a template node is double clicked.
18090          * @param {Roo.View} this
18091          * @param {Number} index The index of the target node
18092          * @param {HTMLElement} node The target node
18093          * @param {Roo.EventObject} e The raw event object
18094          */
18095             "dblclick" : true,
18096         /**
18097          * @event contextmenu
18098          * Fires when a template node is right clicked.
18099          * @param {Roo.View} this
18100          * @param {Number} index The index of the target node
18101          * @param {HTMLElement} node The target node
18102          * @param {Roo.EventObject} e The raw event object
18103          */
18104             "contextmenu" : true,
18105         /**
18106          * @event selectionchange
18107          * Fires when the selected nodes change.
18108          * @param {Roo.View} this
18109          * @param {Array} selections Array of the selected nodes
18110          */
18111             "selectionchange" : true,
18112     
18113         /**
18114          * @event beforeselect
18115          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18116          * @param {Roo.View} this
18117          * @param {HTMLElement} node The node to be selected
18118          * @param {Array} selections Array of currently selected nodes
18119          */
18120             "beforeselect" : true,
18121         /**
18122          * @event preparedata
18123          * Fires on every row to render, to allow you to change the data.
18124          * @param {Roo.View} this
18125          * @param {Object} data to be rendered (change this)
18126          */
18127           "preparedata" : true
18128           
18129           
18130         });
18131
18132
18133
18134     this.el.on({
18135         "click": this.onClick,
18136         "dblclick": this.onDblClick,
18137         "contextmenu": this.onContextMenu,
18138         scope:this
18139     });
18140
18141     this.selections = [];
18142     this.nodes = [];
18143     this.cmp = new Roo.CompositeElementLite([]);
18144     if(this.store){
18145         this.store = Roo.factory(this.store, Roo.data);
18146         this.setStore(this.store, true);
18147     }
18148     
18149     if ( this.footer && this.footer.xtype) {
18150            
18151          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18152         
18153         this.footer.dataSource = this.store;
18154         this.footer.container = fctr;
18155         this.footer = Roo.factory(this.footer, Roo);
18156         fctr.insertFirst(this.el);
18157         
18158         // this is a bit insane - as the paging toolbar seems to detach the el..
18159 //        dom.parentNode.parentNode.parentNode
18160          // they get detached?
18161     }
18162     
18163     
18164     Roo.View.superclass.constructor.call(this);
18165     
18166     
18167 };
18168
18169 Roo.extend(Roo.View, Roo.util.Observable, {
18170     
18171      /**
18172      * @cfg {Roo.data.Store} store Data store to load data from.
18173      */
18174     store : false,
18175     
18176     /**
18177      * @cfg {String|Roo.Element} el The container element.
18178      */
18179     el : '',
18180     
18181     /**
18182      * @cfg {String|Roo.Template} tpl The template used by this View 
18183      */
18184     tpl : false,
18185     /**
18186      * @cfg {String} dataName the named area of the template to use as the data area
18187      *                          Works with domtemplates roo-name="name"
18188      */
18189     dataName: false,
18190     /**
18191      * @cfg {String} selectedClass The css class to add to selected nodes
18192      */
18193     selectedClass : "x-view-selected",
18194      /**
18195      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18196      */
18197     emptyText : "",
18198     
18199     /**
18200      * @cfg {String} text to display on mask (default Loading)
18201      */
18202     mask : false,
18203     /**
18204      * @cfg {Boolean} multiSelect Allow multiple selection
18205      */
18206     multiSelect : false,
18207     /**
18208      * @cfg {Boolean} singleSelect Allow single selection
18209      */
18210     singleSelect:  false,
18211     
18212     /**
18213      * @cfg {Boolean} toggleSelect - selecting 
18214      */
18215     toggleSelect : false,
18216     
18217     /**
18218      * @cfg {Boolean} tickable - selecting 
18219      */
18220     tickable : false,
18221     
18222     /**
18223      * Returns the element this view is bound to.
18224      * @return {Roo.Element}
18225      */
18226     getEl : function(){
18227         return this.wrapEl;
18228     },
18229     
18230     
18231
18232     /**
18233      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18234      */
18235     refresh : function(){
18236         //Roo.log('refresh');
18237         var t = this.tpl;
18238         
18239         // if we are using something like 'domtemplate', then
18240         // the what gets used is:
18241         // t.applySubtemplate(NAME, data, wrapping data..)
18242         // the outer template then get' applied with
18243         //     the store 'extra data'
18244         // and the body get's added to the
18245         //      roo-name="data" node?
18246         //      <span class='roo-tpl-{name}'></span> ?????
18247         
18248         
18249         
18250         this.clearSelections();
18251         this.el.update("");
18252         var html = [];
18253         var records = this.store.getRange();
18254         if(records.length < 1) {
18255             
18256             // is this valid??  = should it render a template??
18257             
18258             this.el.update(this.emptyText);
18259             return;
18260         }
18261         var el = this.el;
18262         if (this.dataName) {
18263             this.el.update(t.apply(this.store.meta)); //????
18264             el = this.el.child('.roo-tpl-' + this.dataName);
18265         }
18266         
18267         for(var i = 0, len = records.length; i < len; i++){
18268             var data = this.prepareData(records[i].data, i, records[i]);
18269             this.fireEvent("preparedata", this, data, i, records[i]);
18270             
18271             var d = Roo.apply({}, data);
18272             
18273             if(this.tickable){
18274                 Roo.apply(d, {'roo-id' : Roo.id()});
18275                 
18276                 var _this = this;
18277             
18278                 Roo.each(this.parent.item, function(item){
18279                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18280                         return;
18281                     }
18282                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18283                 });
18284             }
18285             
18286             html[html.length] = Roo.util.Format.trim(
18287                 this.dataName ?
18288                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18289                     t.apply(d)
18290             );
18291         }
18292         
18293         
18294         
18295         el.update(html.join(""));
18296         this.nodes = el.dom.childNodes;
18297         this.updateIndexes(0);
18298     },
18299     
18300
18301     /**
18302      * Function to override to reformat the data that is sent to
18303      * the template for each node.
18304      * DEPRICATED - use the preparedata event handler.
18305      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18306      * a JSON object for an UpdateManager bound view).
18307      */
18308     prepareData : function(data, index, record)
18309     {
18310         this.fireEvent("preparedata", this, data, index, record);
18311         return data;
18312     },
18313
18314     onUpdate : function(ds, record){
18315         // Roo.log('on update');   
18316         this.clearSelections();
18317         var index = this.store.indexOf(record);
18318         var n = this.nodes[index];
18319         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18320         n.parentNode.removeChild(n);
18321         this.updateIndexes(index, index);
18322     },
18323
18324     
18325     
18326 // --------- FIXME     
18327     onAdd : function(ds, records, index)
18328     {
18329         //Roo.log(['on Add', ds, records, index] );        
18330         this.clearSelections();
18331         if(this.nodes.length == 0){
18332             this.refresh();
18333             return;
18334         }
18335         var n = this.nodes[index];
18336         for(var i = 0, len = records.length; i < len; i++){
18337             var d = this.prepareData(records[i].data, i, records[i]);
18338             if(n){
18339                 this.tpl.insertBefore(n, d);
18340             }else{
18341                 
18342                 this.tpl.append(this.el, d);
18343             }
18344         }
18345         this.updateIndexes(index);
18346     },
18347
18348     onRemove : function(ds, record, index){
18349        // Roo.log('onRemove');
18350         this.clearSelections();
18351         var el = this.dataName  ?
18352             this.el.child('.roo-tpl-' + this.dataName) :
18353             this.el; 
18354         
18355         el.dom.removeChild(this.nodes[index]);
18356         this.updateIndexes(index);
18357     },
18358
18359     /**
18360      * Refresh an individual node.
18361      * @param {Number} index
18362      */
18363     refreshNode : function(index){
18364         this.onUpdate(this.store, this.store.getAt(index));
18365     },
18366
18367     updateIndexes : function(startIndex, endIndex){
18368         var ns = this.nodes;
18369         startIndex = startIndex || 0;
18370         endIndex = endIndex || ns.length - 1;
18371         for(var i = startIndex; i <= endIndex; i++){
18372             ns[i].nodeIndex = i;
18373         }
18374     },
18375
18376     /**
18377      * Changes the data store this view uses and refresh the view.
18378      * @param {Store} store
18379      */
18380     setStore : function(store, initial){
18381         if(!initial && this.store){
18382             this.store.un("datachanged", this.refresh);
18383             this.store.un("add", this.onAdd);
18384             this.store.un("remove", this.onRemove);
18385             this.store.un("update", this.onUpdate);
18386             this.store.un("clear", this.refresh);
18387             this.store.un("beforeload", this.onBeforeLoad);
18388             this.store.un("load", this.onLoad);
18389             this.store.un("loadexception", this.onLoad);
18390         }
18391         if(store){
18392           
18393             store.on("datachanged", this.refresh, this);
18394             store.on("add", this.onAdd, this);
18395             store.on("remove", this.onRemove, this);
18396             store.on("update", this.onUpdate, this);
18397             store.on("clear", this.refresh, this);
18398             store.on("beforeload", this.onBeforeLoad, this);
18399             store.on("load", this.onLoad, this);
18400             store.on("loadexception", this.onLoad, this);
18401         }
18402         
18403         if(store){
18404             this.refresh();
18405         }
18406     },
18407     /**
18408      * onbeforeLoad - masks the loading area.
18409      *
18410      */
18411     onBeforeLoad : function(store,opts)
18412     {
18413          //Roo.log('onBeforeLoad');   
18414         if (!opts.add) {
18415             this.el.update("");
18416         }
18417         this.el.mask(this.mask ? this.mask : "Loading" ); 
18418     },
18419     onLoad : function ()
18420     {
18421         this.el.unmask();
18422     },
18423     
18424
18425     /**
18426      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18427      * @param {HTMLElement} node
18428      * @return {HTMLElement} The template node
18429      */
18430     findItemFromChild : function(node){
18431         var el = this.dataName  ?
18432             this.el.child('.roo-tpl-' + this.dataName,true) :
18433             this.el.dom; 
18434         
18435         if(!node || node.parentNode == el){
18436                     return node;
18437             }
18438             var p = node.parentNode;
18439             while(p && p != el){
18440             if(p.parentNode == el){
18441                 return p;
18442             }
18443             p = p.parentNode;
18444         }
18445             return null;
18446     },
18447
18448     /** @ignore */
18449     onClick : function(e){
18450         var item = this.findItemFromChild(e.getTarget());
18451         if(item){
18452             var index = this.indexOf(item);
18453             if(this.onItemClick(item, index, e) !== false){
18454                 this.fireEvent("click", this, index, item, e);
18455             }
18456         }else{
18457             this.clearSelections();
18458         }
18459     },
18460
18461     /** @ignore */
18462     onContextMenu : function(e){
18463         var item = this.findItemFromChild(e.getTarget());
18464         if(item){
18465             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18466         }
18467     },
18468
18469     /** @ignore */
18470     onDblClick : function(e){
18471         var item = this.findItemFromChild(e.getTarget());
18472         if(item){
18473             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18474         }
18475     },
18476
18477     onItemClick : function(item, index, e)
18478     {
18479         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18480             return false;
18481         }
18482         if (this.toggleSelect) {
18483             var m = this.isSelected(item) ? 'unselect' : 'select';
18484             //Roo.log(m);
18485             var _t = this;
18486             _t[m](item, true, false);
18487             return true;
18488         }
18489         if(this.multiSelect || this.singleSelect){
18490             if(this.multiSelect && e.shiftKey && this.lastSelection){
18491                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18492             }else{
18493                 this.select(item, this.multiSelect && e.ctrlKey);
18494                 this.lastSelection = item;
18495             }
18496             
18497             if(!this.tickable){
18498                 e.preventDefault();
18499             }
18500             
18501         }
18502         return true;
18503     },
18504
18505     /**
18506      * Get the number of selected nodes.
18507      * @return {Number}
18508      */
18509     getSelectionCount : function(){
18510         return this.selections.length;
18511     },
18512
18513     /**
18514      * Get the currently selected nodes.
18515      * @return {Array} An array of HTMLElements
18516      */
18517     getSelectedNodes : function(){
18518         return this.selections;
18519     },
18520
18521     /**
18522      * Get the indexes of the selected nodes.
18523      * @return {Array}
18524      */
18525     getSelectedIndexes : function(){
18526         var indexes = [], s = this.selections;
18527         for(var i = 0, len = s.length; i < len; i++){
18528             indexes.push(s[i].nodeIndex);
18529         }
18530         return indexes;
18531     },
18532
18533     /**
18534      * Clear all selections
18535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18536      */
18537     clearSelections : function(suppressEvent){
18538         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18539             this.cmp.elements = this.selections;
18540             this.cmp.removeClass(this.selectedClass);
18541             this.selections = [];
18542             if(!suppressEvent){
18543                 this.fireEvent("selectionchange", this, this.selections);
18544             }
18545         }
18546     },
18547
18548     /**
18549      * Returns true if the passed node is selected
18550      * @param {HTMLElement/Number} node The node or node index
18551      * @return {Boolean}
18552      */
18553     isSelected : function(node){
18554         var s = this.selections;
18555         if(s.length < 1){
18556             return false;
18557         }
18558         node = this.getNode(node);
18559         return s.indexOf(node) !== -1;
18560     },
18561
18562     /**
18563      * Selects nodes.
18564      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18565      * @param {Boolean} keepExisting (optional) true to keep existing selections
18566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18567      */
18568     select : function(nodeInfo, keepExisting, suppressEvent){
18569         if(nodeInfo instanceof Array){
18570             if(!keepExisting){
18571                 this.clearSelections(true);
18572             }
18573             for(var i = 0, len = nodeInfo.length; i < len; i++){
18574                 this.select(nodeInfo[i], true, true);
18575             }
18576             return;
18577         } 
18578         var node = this.getNode(nodeInfo);
18579         if(!node || this.isSelected(node)){
18580             return; // already selected.
18581         }
18582         if(!keepExisting){
18583             this.clearSelections(true);
18584         }
18585         
18586         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18587             Roo.fly(node).addClass(this.selectedClass);
18588             this.selections.push(node);
18589             if(!suppressEvent){
18590                 this.fireEvent("selectionchange", this, this.selections);
18591             }
18592         }
18593         
18594         
18595     },
18596       /**
18597      * Unselects nodes.
18598      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18599      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18600      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18601      */
18602     unselect : function(nodeInfo, keepExisting, suppressEvent)
18603     {
18604         if(nodeInfo instanceof Array){
18605             Roo.each(this.selections, function(s) {
18606                 this.unselect(s, nodeInfo);
18607             }, this);
18608             return;
18609         }
18610         var node = this.getNode(nodeInfo);
18611         if(!node || !this.isSelected(node)){
18612             //Roo.log("not selected");
18613             return; // not selected.
18614         }
18615         // fireevent???
18616         var ns = [];
18617         Roo.each(this.selections, function(s) {
18618             if (s == node ) {
18619                 Roo.fly(node).removeClass(this.selectedClass);
18620
18621                 return;
18622             }
18623             ns.push(s);
18624         },this);
18625         
18626         this.selections= ns;
18627         this.fireEvent("selectionchange", this, this.selections);
18628     },
18629
18630     /**
18631      * Gets a template node.
18632      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18633      * @return {HTMLElement} The node or null if it wasn't found
18634      */
18635     getNode : function(nodeInfo){
18636         if(typeof nodeInfo == "string"){
18637             return document.getElementById(nodeInfo);
18638         }else if(typeof nodeInfo == "number"){
18639             return this.nodes[nodeInfo];
18640         }
18641         return nodeInfo;
18642     },
18643
18644     /**
18645      * Gets a range template nodes.
18646      * @param {Number} startIndex
18647      * @param {Number} endIndex
18648      * @return {Array} An array of nodes
18649      */
18650     getNodes : function(start, end){
18651         var ns = this.nodes;
18652         start = start || 0;
18653         end = typeof end == "undefined" ? ns.length - 1 : end;
18654         var nodes = [];
18655         if(start <= end){
18656             for(var i = start; i <= end; i++){
18657                 nodes.push(ns[i]);
18658             }
18659         } else{
18660             for(var i = start; i >= end; i--){
18661                 nodes.push(ns[i]);
18662             }
18663         }
18664         return nodes;
18665     },
18666
18667     /**
18668      * Finds the index of the passed node
18669      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18670      * @return {Number} The index of the node or -1
18671      */
18672     indexOf : function(node){
18673         node = this.getNode(node);
18674         if(typeof node.nodeIndex == "number"){
18675             return node.nodeIndex;
18676         }
18677         var ns = this.nodes;
18678         for(var i = 0, len = ns.length; i < len; i++){
18679             if(ns[i] == node){
18680                 return i;
18681             }
18682         }
18683         return -1;
18684     }
18685 });
18686 /*
18687  * - LGPL
18688  *
18689  * based on jquery fullcalendar
18690  * 
18691  */
18692
18693 Roo.bootstrap = Roo.bootstrap || {};
18694 /**
18695  * @class Roo.bootstrap.Calendar
18696  * @extends Roo.bootstrap.Component
18697  * Bootstrap Calendar class
18698  * @cfg {Boolean} loadMask (true|false) default false
18699  * @cfg {Object} header generate the user specific header of the calendar, default false
18700
18701  * @constructor
18702  * Create a new Container
18703  * @param {Object} config The config object
18704  */
18705
18706
18707
18708 Roo.bootstrap.Calendar = function(config){
18709     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18710      this.addEvents({
18711         /**
18712              * @event select
18713              * Fires when a date is selected
18714              * @param {DatePicker} this
18715              * @param {Date} date The selected date
18716              */
18717         'select': true,
18718         /**
18719              * @event monthchange
18720              * Fires when the displayed month changes 
18721              * @param {DatePicker} this
18722              * @param {Date} date The selected month
18723              */
18724         'monthchange': true,
18725         /**
18726              * @event evententer
18727              * Fires when mouse over an event
18728              * @param {Calendar} this
18729              * @param {event} Event
18730              */
18731         'evententer': true,
18732         /**
18733              * @event eventleave
18734              * Fires when the mouse leaves an
18735              * @param {Calendar} this
18736              * @param {event}
18737              */
18738         'eventleave': true,
18739         /**
18740              * @event eventclick
18741              * Fires when the mouse click an
18742              * @param {Calendar} this
18743              * @param {event}
18744              */
18745         'eventclick': true
18746         
18747     });
18748
18749 };
18750
18751 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18752     
18753      /**
18754      * @cfg {Number} startDay
18755      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18756      */
18757     startDay : 0,
18758     
18759     loadMask : false,
18760     
18761     header : false,
18762       
18763     getAutoCreate : function(){
18764         
18765         
18766         var fc_button = function(name, corner, style, content ) {
18767             return Roo.apply({},{
18768                 tag : 'span',
18769                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18770                          (corner.length ?
18771                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18772                             ''
18773                         ),
18774                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18775                 unselectable: 'on'
18776             });
18777         };
18778         
18779         var header = {};
18780         
18781         if(!this.header){
18782             header = {
18783                 tag : 'table',
18784                 cls : 'fc-header',
18785                 style : 'width:100%',
18786                 cn : [
18787                     {
18788                         tag: 'tr',
18789                         cn : [
18790                             {
18791                                 tag : 'td',
18792                                 cls : 'fc-header-left',
18793                                 cn : [
18794                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18795                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18796                                     { tag: 'span', cls: 'fc-header-space' },
18797                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18798
18799
18800                                 ]
18801                             },
18802
18803                             {
18804                                 tag : 'td',
18805                                 cls : 'fc-header-center',
18806                                 cn : [
18807                                     {
18808                                         tag: 'span',
18809                                         cls: 'fc-header-title',
18810                                         cn : {
18811                                             tag: 'H2',
18812                                             html : 'month / year'
18813                                         }
18814                                     }
18815
18816                                 ]
18817                             },
18818                             {
18819                                 tag : 'td',
18820                                 cls : 'fc-header-right',
18821                                 cn : [
18822                               /*      fc_button('month', 'left', '', 'month' ),
18823                                     fc_button('week', '', '', 'week' ),
18824                                     fc_button('day', 'right', '', 'day' )
18825                                 */    
18826
18827                                 ]
18828                             }
18829
18830                         ]
18831                     }
18832                 ]
18833             };
18834         }
18835         
18836         header = this.header;
18837         
18838        
18839         var cal_heads = function() {
18840             var ret = [];
18841             // fixme - handle this.
18842             
18843             for (var i =0; i < Date.dayNames.length; i++) {
18844                 var d = Date.dayNames[i];
18845                 ret.push({
18846                     tag: 'th',
18847                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18848                     html : d.substring(0,3)
18849                 });
18850                 
18851             }
18852             ret[0].cls += ' fc-first';
18853             ret[6].cls += ' fc-last';
18854             return ret;
18855         };
18856         var cal_cell = function(n) {
18857             return  {
18858                 tag: 'td',
18859                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18860                 cn : [
18861                     {
18862                         cn : [
18863                             {
18864                                 cls: 'fc-day-number',
18865                                 html: 'D'
18866                             },
18867                             {
18868                                 cls: 'fc-day-content',
18869                              
18870                                 cn : [
18871                                      {
18872                                         style: 'position: relative;' // height: 17px;
18873                                     }
18874                                 ]
18875                             }
18876                             
18877                             
18878                         ]
18879                     }
18880                 ]
18881                 
18882             }
18883         };
18884         var cal_rows = function() {
18885             
18886             var ret = [];
18887             for (var r = 0; r < 6; r++) {
18888                 var row= {
18889                     tag : 'tr',
18890                     cls : 'fc-week',
18891                     cn : []
18892                 };
18893                 
18894                 for (var i =0; i < Date.dayNames.length; i++) {
18895                     var d = Date.dayNames[i];
18896                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18897
18898                 }
18899                 row.cn[0].cls+=' fc-first';
18900                 row.cn[0].cn[0].style = 'min-height:90px';
18901                 row.cn[6].cls+=' fc-last';
18902                 ret.push(row);
18903                 
18904             }
18905             ret[0].cls += ' fc-first';
18906             ret[4].cls += ' fc-prev-last';
18907             ret[5].cls += ' fc-last';
18908             return ret;
18909             
18910         };
18911         
18912         var cal_table = {
18913             tag: 'table',
18914             cls: 'fc-border-separate',
18915             style : 'width:100%',
18916             cellspacing  : 0,
18917             cn : [
18918                 { 
18919                     tag: 'thead',
18920                     cn : [
18921                         { 
18922                             tag: 'tr',
18923                             cls : 'fc-first fc-last',
18924                             cn : cal_heads()
18925                         }
18926                     ]
18927                 },
18928                 { 
18929                     tag: 'tbody',
18930                     cn : cal_rows()
18931                 }
18932                   
18933             ]
18934         };
18935          
18936          var cfg = {
18937             cls : 'fc fc-ltr',
18938             cn : [
18939                 header,
18940                 {
18941                     cls : 'fc-content',
18942                     style : "position: relative;",
18943                     cn : [
18944                         {
18945                             cls : 'fc-view fc-view-month fc-grid',
18946                             style : 'position: relative',
18947                             unselectable : 'on',
18948                             cn : [
18949                                 {
18950                                     cls : 'fc-event-container',
18951                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18952                                 },
18953                                 cal_table
18954                             ]
18955                         }
18956                     ]
18957     
18958                 }
18959            ] 
18960             
18961         };
18962         
18963          
18964         
18965         return cfg;
18966     },
18967     
18968     
18969     initEvents : function()
18970     {
18971         if(!this.store){
18972             throw "can not find store for calendar";
18973         }
18974         
18975         var mark = {
18976             tag: "div",
18977             cls:"x-dlg-mask",
18978             style: "text-align:center",
18979             cn: [
18980                 {
18981                     tag: "div",
18982                     style: "background-color:white;width:50%;margin:250 auto",
18983                     cn: [
18984                         {
18985                             tag: "img",
18986                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18987                         },
18988                         {
18989                             tag: "span",
18990                             html: "Loading"
18991                         }
18992                         
18993                     ]
18994                 }
18995             ]
18996         };
18997         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18998         
18999         var size = this.el.select('.fc-content', true).first().getSize();
19000         this.maskEl.setSize(size.width, size.height);
19001         this.maskEl.enableDisplayMode("block");
19002         if(!this.loadMask){
19003             this.maskEl.hide();
19004         }
19005         
19006         this.store = Roo.factory(this.store, Roo.data);
19007         this.store.on('load', this.onLoad, this);
19008         this.store.on('beforeload', this.onBeforeLoad, this);
19009         
19010         this.resize();
19011         
19012         this.cells = this.el.select('.fc-day',true);
19013         //Roo.log(this.cells);
19014         this.textNodes = this.el.query('.fc-day-number');
19015         this.cells.addClassOnOver('fc-state-hover');
19016         
19017         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19018         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19019         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19020         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19021         
19022         this.on('monthchange', this.onMonthChange, this);
19023         
19024         this.update(new Date().clearTime());
19025     },
19026     
19027     resize : function() {
19028         var sz  = this.el.getSize();
19029         
19030         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19031         this.el.select('.fc-day-content div',true).setHeight(34);
19032     },
19033     
19034     
19035     // private
19036     showPrevMonth : function(e){
19037         this.update(this.activeDate.add("mo", -1));
19038     },
19039     showToday : function(e){
19040         this.update(new Date().clearTime());
19041     },
19042     // private
19043     showNextMonth : function(e){
19044         this.update(this.activeDate.add("mo", 1));
19045     },
19046
19047     // private
19048     showPrevYear : function(){
19049         this.update(this.activeDate.add("y", -1));
19050     },
19051
19052     // private
19053     showNextYear : function(){
19054         this.update(this.activeDate.add("y", 1));
19055     },
19056
19057     
19058    // private
19059     update : function(date)
19060     {
19061         var vd = this.activeDate;
19062         this.activeDate = date;
19063 //        if(vd && this.el){
19064 //            var t = date.getTime();
19065 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19066 //                Roo.log('using add remove');
19067 //                
19068 //                this.fireEvent('monthchange', this, date);
19069 //                
19070 //                this.cells.removeClass("fc-state-highlight");
19071 //                this.cells.each(function(c){
19072 //                   if(c.dateValue == t){
19073 //                       c.addClass("fc-state-highlight");
19074 //                       setTimeout(function(){
19075 //                            try{c.dom.firstChild.focus();}catch(e){}
19076 //                       }, 50);
19077 //                       return false;
19078 //                   }
19079 //                   return true;
19080 //                });
19081 //                return;
19082 //            }
19083 //        }
19084         
19085         var days = date.getDaysInMonth();
19086         
19087         var firstOfMonth = date.getFirstDateOfMonth();
19088         var startingPos = firstOfMonth.getDay()-this.startDay;
19089         
19090         if(startingPos < this.startDay){
19091             startingPos += 7;
19092         }
19093         
19094         var pm = date.add(Date.MONTH, -1);
19095         var prevStart = pm.getDaysInMonth()-startingPos;
19096 //        
19097         this.cells = this.el.select('.fc-day',true);
19098         this.textNodes = this.el.query('.fc-day-number');
19099         this.cells.addClassOnOver('fc-state-hover');
19100         
19101         var cells = this.cells.elements;
19102         var textEls = this.textNodes;
19103         
19104         Roo.each(cells, function(cell){
19105             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19106         });
19107         
19108         days += startingPos;
19109
19110         // convert everything to numbers so it's fast
19111         var day = 86400000;
19112         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19113         //Roo.log(d);
19114         //Roo.log(pm);
19115         //Roo.log(prevStart);
19116         
19117         var today = new Date().clearTime().getTime();
19118         var sel = date.clearTime().getTime();
19119         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19120         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19121         var ddMatch = this.disabledDatesRE;
19122         var ddText = this.disabledDatesText;
19123         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19124         var ddaysText = this.disabledDaysText;
19125         var format = this.format;
19126         
19127         var setCellClass = function(cal, cell){
19128             cell.row = 0;
19129             cell.events = [];
19130             cell.more = [];
19131             //Roo.log('set Cell Class');
19132             cell.title = "";
19133             var t = d.getTime();
19134             
19135             //Roo.log(d);
19136             
19137             cell.dateValue = t;
19138             if(t == today){
19139                 cell.className += " fc-today";
19140                 cell.className += " fc-state-highlight";
19141                 cell.title = cal.todayText;
19142             }
19143             if(t == sel){
19144                 // disable highlight in other month..
19145                 //cell.className += " fc-state-highlight";
19146                 
19147             }
19148             // disabling
19149             if(t < min) {
19150                 cell.className = " fc-state-disabled";
19151                 cell.title = cal.minText;
19152                 return;
19153             }
19154             if(t > max) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.maxText;
19157                 return;
19158             }
19159             if(ddays){
19160                 if(ddays.indexOf(d.getDay()) != -1){
19161                     cell.title = ddaysText;
19162                     cell.className = " fc-state-disabled";
19163                 }
19164             }
19165             if(ddMatch && format){
19166                 var fvalue = d.dateFormat(format);
19167                 if(ddMatch.test(fvalue)){
19168                     cell.title = ddText.replace("%0", fvalue);
19169                     cell.className = " fc-state-disabled";
19170                 }
19171             }
19172             
19173             if (!cell.initialClassName) {
19174                 cell.initialClassName = cell.dom.className;
19175             }
19176             
19177             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19178         };
19179
19180         var i = 0;
19181         
19182         for(; i < startingPos; i++) {
19183             textEls[i].innerHTML = (++prevStart);
19184             d.setDate(d.getDate()+1);
19185             
19186             cells[i].className = "fc-past fc-other-month";
19187             setCellClass(this, cells[i]);
19188         }
19189         
19190         var intDay = 0;
19191         
19192         for(; i < days; i++){
19193             intDay = i - startingPos + 1;
19194             textEls[i].innerHTML = (intDay);
19195             d.setDate(d.getDate()+1);
19196             
19197             cells[i].className = ''; // "x-date-active";
19198             setCellClass(this, cells[i]);
19199         }
19200         var extraDays = 0;
19201         
19202         for(; i < 42; i++) {
19203             textEls[i].innerHTML = (++extraDays);
19204             d.setDate(d.getDate()+1);
19205             
19206             cells[i].className = "fc-future fc-other-month";
19207             setCellClass(this, cells[i]);
19208         }
19209         
19210         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19211         
19212         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19213         
19214         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19215         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19216         
19217         if(totalRows != 6){
19218             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19219             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19220         }
19221         
19222         this.fireEvent('monthchange', this, date);
19223         
19224         
19225         /*
19226         if(!this.internalRender){
19227             var main = this.el.dom.firstChild;
19228             var w = main.offsetWidth;
19229             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19230             Roo.fly(main).setWidth(w);
19231             this.internalRender = true;
19232             // opera does not respect the auto grow header center column
19233             // then, after it gets a width opera refuses to recalculate
19234             // without a second pass
19235             if(Roo.isOpera && !this.secondPass){
19236                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19237                 this.secondPass = true;
19238                 this.update.defer(10, this, [date]);
19239             }
19240         }
19241         */
19242         
19243     },
19244     
19245     findCell : function(dt) {
19246         dt = dt.clearTime().getTime();
19247         var ret = false;
19248         this.cells.each(function(c){
19249             //Roo.log("check " +c.dateValue + '?=' + dt);
19250             if(c.dateValue == dt){
19251                 ret = c;
19252                 return false;
19253             }
19254             return true;
19255         });
19256         
19257         return ret;
19258     },
19259     
19260     findCells : function(ev) {
19261         var s = ev.start.clone().clearTime().getTime();
19262        // Roo.log(s);
19263         var e= ev.end.clone().clearTime().getTime();
19264        // Roo.log(e);
19265         var ret = [];
19266         this.cells.each(function(c){
19267              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19268             
19269             if(c.dateValue > e){
19270                 return ;
19271             }
19272             if(c.dateValue < s){
19273                 return ;
19274             }
19275             ret.push(c);
19276         });
19277         
19278         return ret;    
19279     },
19280     
19281 //    findBestRow: function(cells)
19282 //    {
19283 //        var ret = 0;
19284 //        
19285 //        for (var i =0 ; i < cells.length;i++) {
19286 //            ret  = Math.max(cells[i].rows || 0,ret);
19287 //        }
19288 //        return ret;
19289 //        
19290 //    },
19291     
19292     
19293     addItem : function(ev)
19294     {
19295         // look for vertical location slot in
19296         var cells = this.findCells(ev);
19297         
19298 //        ev.row = this.findBestRow(cells);
19299         
19300         // work out the location.
19301         
19302         var crow = false;
19303         var rows = [];
19304         for(var i =0; i < cells.length; i++) {
19305             
19306             cells[i].row = cells[0].row;
19307             
19308             if(i == 0){
19309                 cells[i].row = cells[i].row + 1;
19310             }
19311             
19312             if (!crow) {
19313                 crow = {
19314                     start : cells[i],
19315                     end :  cells[i]
19316                 };
19317                 continue;
19318             }
19319             if (crow.start.getY() == cells[i].getY()) {
19320                 // on same row.
19321                 crow.end = cells[i];
19322                 continue;
19323             }
19324             // different row.
19325             rows.push(crow);
19326             crow = {
19327                 start: cells[i],
19328                 end : cells[i]
19329             };
19330             
19331         }
19332         
19333         rows.push(crow);
19334         ev.els = [];
19335         ev.rows = rows;
19336         ev.cells = cells;
19337         
19338         cells[0].events.push(ev);
19339         
19340         this.calevents.push(ev);
19341     },
19342     
19343     clearEvents: function() {
19344         
19345         if(!this.calevents){
19346             return;
19347         }
19348         
19349         Roo.each(this.cells.elements, function(c){
19350             c.row = 0;
19351             c.events = [];
19352             c.more = [];
19353         });
19354         
19355         Roo.each(this.calevents, function(e) {
19356             Roo.each(e.els, function(el) {
19357                 el.un('mouseenter' ,this.onEventEnter, this);
19358                 el.un('mouseleave' ,this.onEventLeave, this);
19359                 el.remove();
19360             },this);
19361         },this);
19362         
19363         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19364             e.remove();
19365         });
19366         
19367     },
19368     
19369     renderEvents: function()
19370     {   
19371         var _this = this;
19372         
19373         this.cells.each(function(c) {
19374             
19375             if(c.row < 5){
19376                 return;
19377             }
19378             
19379             var ev = c.events;
19380             
19381             var r = 4;
19382             if(c.row != c.events.length){
19383                 r = 4 - (4 - (c.row - c.events.length));
19384             }
19385             
19386             c.events = ev.slice(0, r);
19387             c.more = ev.slice(r);
19388             
19389             if(c.more.length && c.more.length == 1){
19390                 c.events.push(c.more.pop());
19391             }
19392             
19393             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19394             
19395         });
19396             
19397         this.cells.each(function(c) {
19398             
19399             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19400             
19401             
19402             for (var e = 0; e < c.events.length; e++){
19403                 var ev = c.events[e];
19404                 var rows = ev.rows;
19405                 
19406                 for(var i = 0; i < rows.length; i++) {
19407                 
19408                     // how many rows should it span..
19409
19410                     var  cfg = {
19411                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19412                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19413
19414                         unselectable : "on",
19415                         cn : [
19416                             {
19417                                 cls: 'fc-event-inner',
19418                                 cn : [
19419     //                                {
19420     //                                  tag:'span',
19421     //                                  cls: 'fc-event-time',
19422     //                                  html : cells.length > 1 ? '' : ev.time
19423     //                                },
19424                                     {
19425                                       tag:'span',
19426                                       cls: 'fc-event-title',
19427                                       html : String.format('{0}', ev.title)
19428                                     }
19429
19430
19431                                 ]
19432                             },
19433                             {
19434                                 cls: 'ui-resizable-handle ui-resizable-e',
19435                                 html : '&nbsp;&nbsp;&nbsp'
19436                             }
19437
19438                         ]
19439                     };
19440
19441                     if (i == 0) {
19442                         cfg.cls += ' fc-event-start';
19443                     }
19444                     if ((i+1) == rows.length) {
19445                         cfg.cls += ' fc-event-end';
19446                     }
19447
19448                     var ctr = _this.el.select('.fc-event-container',true).first();
19449                     var cg = ctr.createChild(cfg);
19450
19451                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19452                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19453
19454                     var r = (c.more.length) ? 1 : 0;
19455                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19456                     cg.setWidth(ebox.right - sbox.x -2);
19457
19458                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19459                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19460                     cg.on('click', _this.onEventClick, _this, ev);
19461
19462                     ev.els.push(cg);
19463                     
19464                 }
19465                 
19466             }
19467             
19468             
19469             if(c.more.length){
19470                 var  cfg = {
19471                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19472                     style : 'position: absolute',
19473                     unselectable : "on",
19474                     cn : [
19475                         {
19476                             cls: 'fc-event-inner',
19477                             cn : [
19478                                 {
19479                                   tag:'span',
19480                                   cls: 'fc-event-title',
19481                                   html : 'More'
19482                                 }
19483
19484
19485                             ]
19486                         },
19487                         {
19488                             cls: 'ui-resizable-handle ui-resizable-e',
19489                             html : '&nbsp;&nbsp;&nbsp'
19490                         }
19491
19492                     ]
19493                 };
19494
19495                 var ctr = _this.el.select('.fc-event-container',true).first();
19496                 var cg = ctr.createChild(cfg);
19497
19498                 var sbox = c.select('.fc-day-content',true).first().getBox();
19499                 var ebox = c.select('.fc-day-content',true).first().getBox();
19500                 //Roo.log(cg);
19501                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19502                 cg.setWidth(ebox.right - sbox.x -2);
19503
19504                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19505                 
19506             }
19507             
19508         });
19509         
19510         
19511         
19512     },
19513     
19514     onEventEnter: function (e, el,event,d) {
19515         this.fireEvent('evententer', this, el, event);
19516     },
19517     
19518     onEventLeave: function (e, el,event,d) {
19519         this.fireEvent('eventleave', this, el, event);
19520     },
19521     
19522     onEventClick: function (e, el,event,d) {
19523         this.fireEvent('eventclick', this, el, event);
19524     },
19525     
19526     onMonthChange: function () {
19527         this.store.load();
19528     },
19529     
19530     onMoreEventClick: function(e, el, more)
19531     {
19532         var _this = this;
19533         
19534         this.calpopover.placement = 'right';
19535         this.calpopover.setTitle('More');
19536         
19537         this.calpopover.setContent('');
19538         
19539         var ctr = this.calpopover.el.select('.popover-content', true).first();
19540         
19541         Roo.each(more, function(m){
19542             var cfg = {
19543                 cls : 'fc-event-hori fc-event-draggable',
19544                 html : m.title
19545             };
19546             var cg = ctr.createChild(cfg);
19547             
19548             cg.on('click', _this.onEventClick, _this, m);
19549         });
19550         
19551         this.calpopover.show(el);
19552         
19553         
19554     },
19555     
19556     onLoad: function () 
19557     {   
19558         this.calevents = [];
19559         var cal = this;
19560         
19561         if(this.store.getCount() > 0){
19562             this.store.data.each(function(d){
19563                cal.addItem({
19564                     id : d.data.id,
19565                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19566                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19567                     time : d.data.start_time,
19568                     title : d.data.title,
19569                     description : d.data.description,
19570                     venue : d.data.venue
19571                 });
19572             });
19573         }
19574         
19575         this.renderEvents();
19576         
19577         if(this.calevents.length && this.loadMask){
19578             this.maskEl.hide();
19579         }
19580     },
19581     
19582     onBeforeLoad: function()
19583     {
19584         this.clearEvents();
19585         if(this.loadMask){
19586             this.maskEl.show();
19587         }
19588     }
19589 });
19590
19591  
19592  /*
19593  * - LGPL
19594  *
19595  * element
19596  * 
19597  */
19598
19599 /**
19600  * @class Roo.bootstrap.Popover
19601  * @extends Roo.bootstrap.Component
19602  * Bootstrap Popover class
19603  * @cfg {String} html contents of the popover   (or false to use children..)
19604  * @cfg {String} title of popover (or false to hide)
19605  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19606  * @cfg {String} trigger click || hover (or false to trigger manually)
19607  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19608  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19609  *      - if false and it has a 'parent' then it will be automatically added to that element
19610  *      - if string - Roo.get  will be called 
19611  * @cfg {Number} delay - delay before showing
19612  
19613  * @constructor
19614  * Create a new Popover
19615  * @param {Object} config The config object
19616  */
19617
19618 Roo.bootstrap.Popover = function(config){
19619     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19620     
19621     this.addEvents({
19622         // raw events
19623          /**
19624          * @event show
19625          * After the popover show
19626          * 
19627          * @param {Roo.bootstrap.Popover} this
19628          */
19629         "show" : true,
19630         /**
19631          * @event hide
19632          * After the popover hide
19633          * 
19634          * @param {Roo.bootstrap.Popover} this
19635          */
19636         "hide" : true
19637     });
19638 };
19639
19640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19641     
19642     title: false,
19643     html: false,
19644     
19645     placement : 'right',
19646     trigger : 'hover', // hover
19647     modal : false,
19648     delay : 0,
19649     
19650     over: false,
19651     
19652     can_build_overlaid : false,
19653     
19654     maskEl : false, // the mask element
19655     headerEl : false,
19656     contentEl : false,
19657     
19658     
19659     getChildContainer : function()
19660     {
19661         return this.contentEl;
19662         
19663     },
19664     getPopoverHeader : function()
19665     {
19666         return this.headerEl
19667     },
19668     
19669     
19670     getAutoCreate : function(){
19671          
19672         var cfg = {
19673            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19674            style: 'display:block',
19675            cn : [
19676                 {
19677                     cls : 'arrow'
19678                 },
19679                 {
19680                     cls : 'popover-inner ',
19681                     cn : [
19682                         {
19683                             tag: 'h3',
19684                             cls: 'popover-title popover-header',
19685                             html : this.title || ''
19686                         },
19687                         {
19688                             cls : 'popover-content popover-body'  + this.cls,
19689                             html : this.html || ''
19690                         }
19691                     ]
19692                     
19693                 }
19694            ]
19695         };
19696         
19697         return cfg;
19698     },
19699     /**
19700      * @param {string} the title
19701      */
19702     setTitle: function(str)
19703     {
19704         this.title = str;
19705         if (this.el) {
19706             this.headerEl.dom.innerHTML = str;
19707         }
19708         
19709     },
19710     /**
19711      * @param {string} the body content
19712      */
19713     setContent: function(str)
19714     {
19715         this.html = str;
19716         if (this.contentEl) {
19717             this.contentEl.dom.innerHTML = str;
19718         }
19719         
19720     },
19721     // as it get's added to the bottom of the page.
19722     onRender : function(ct, position)
19723     {
19724         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19725         if(!this.el){
19726             var cfg = Roo.apply({},  this.getAutoCreate());
19727             cfg.id = Roo.id();
19728             
19729             if (this.cls) {
19730                 cfg.cls += ' ' + this.cls;
19731             }
19732             if (this.style) {
19733                 cfg.style = this.style;
19734             }
19735             //Roo.log("adding to ");
19736             this.el = Roo.get(document.body).createChild(cfg, position);
19737 //            Roo.log(this.el);
19738         }
19739         
19740         var nitems = [];
19741         if(typeof(this.items) != 'undefined'){
19742             var items = this.items;
19743             delete this.items;
19744
19745             for(var i =0;i < items.length;i++) {
19746                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19747             }
19748         }
19749
19750         this.items = nitems;
19751         
19752         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19753         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19754         
19755         
19756         this.contentEl = this.el.select('.popover-content',true).first();
19757         this.headerEl =  this.el.select('.popover-header',true).first();
19758         
19759         this.initEvents();
19760     },
19761     
19762     resizeMask : function()
19763     {
19764         this.maskEl.setSize(
19765             Roo.lib.Dom.getViewWidth(true),
19766             Roo.lib.Dom.getViewHeight(true)
19767         );
19768     },
19769     
19770     initEvents : function()
19771     {
19772         
19773         if (!this.modal) { 
19774             Roo.bootstrap.Popover.register(this);
19775         }
19776          
19777         
19778         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19779         this.el.enableDisplayMode('block');
19780         this.el.hide();
19781         if (this.over === false && !this.parent()) {
19782             return; 
19783         }
19784         if (this.triggers === false) {
19785             return;
19786         }
19787          
19788         // support parent
19789         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19790         var triggers = this.trigger ? this.trigger.split(' ') : [];
19791         Roo.each(triggers, function(trigger) {
19792         
19793             if (trigger == 'click') {
19794                 on_el.on('click', this.toggle, this);
19795             } else if (trigger != 'manual') {
19796                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19797                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19798       
19799                 on_el.on(eventIn  ,this.enter, this);
19800                 on_el.on(eventOut, this.leave, this);
19801             }
19802         }, this);
19803         
19804     },
19805     
19806     
19807     // private
19808     timeout : null,
19809     hoverState : null,
19810     
19811     toggle : function () {
19812         this.hoverState == 'in' ? this.leave() : this.enter();
19813     },
19814     
19815     enter : function () {
19816         
19817         clearTimeout(this.timeout);
19818     
19819         this.hoverState = 'in';
19820     
19821         if (!this.delay || !this.delay.show) {
19822             this.show();
19823             return;
19824         }
19825         var _t = this;
19826         this.timeout = setTimeout(function () {
19827             if (_t.hoverState == 'in') {
19828                 _t.show();
19829             }
19830         }, this.delay.show)
19831     },
19832     
19833     leave : function() {
19834         clearTimeout(this.timeout);
19835     
19836         this.hoverState = 'out';
19837     
19838         if (!this.delay || !this.delay.hide) {
19839             this.hide();
19840             return;
19841         }
19842         var _t = this;
19843         this.timeout = setTimeout(function () {
19844             if (_t.hoverState == 'out') {
19845                 _t.hide();
19846             }
19847         }, this.delay.hide)
19848     },
19849     /**
19850      * Show the popover
19851      * @param {Roo.Element|string|false} - element to align and point to.
19852      */
19853     show : function (on_el)
19854     {
19855         
19856         on_el = on_el || false; // default to false
19857         if (!on_el) {
19858             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19859                 on_el = this.parent().el;
19860             } else if (this.over) {
19861                 Roo.get(this.over);
19862             }
19863             
19864         }
19865         
19866         if (!this.el) {
19867             this.render(document.body);
19868         }
19869         
19870         
19871         this.el.removeClass([
19872             'fade','top','bottom', 'left', 'right','in',
19873             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19874         ]);
19875         
19876         if (this.title === false) {
19877             this.headerEl.hide();
19878         }
19879         
19880         
19881         var placement = typeof this.placement == 'function' ?
19882             this.placement.call(this, this.el, on_el) :
19883             this.placement;
19884             
19885         /*
19886         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19887         
19888         // I think  'auto right' - but 
19889         
19890         var autoPlace = autoToken.test(placement);
19891         if (autoPlace) {
19892             placement = placement.replace(autoToken, '') || 'top';
19893         }
19894         */
19895         
19896         
19897         this.el.show();
19898         this.el.dom.style.display='block';
19899         
19900         //this.el.appendTo(on_el);
19901         
19902         var p = this.getPosition();
19903         var box = this.el.getBox();
19904         
19905         
19906         var align = Roo.bootstrap.Popover.alignment[placement];
19907         this.el.addClass(align[2]);
19908
19909 //        Roo.log(align);
19910
19911         if (on_el) {
19912             this.el.alignTo(on_el, align[0],align[1]);
19913         } else {
19914             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19915             var es = this.el.getSize();
19916             var x = Roo.lib.Dom.getViewWidth()/2;
19917             var y = Roo.lib.Dom.getViewHeight()/2;
19918             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19919             
19920         }
19921
19922         
19923         //var arrow = this.el.select('.arrow',true).first();
19924         //arrow.set(align[2], 
19925         
19926         this.el.addClass('in');
19927         
19928         
19929         if (this.el.hasClass('fade')) {
19930             // fade it?
19931         }
19932         
19933         this.hoverState = 'in';
19934         
19935         if (this.modal) {
19936             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19937             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19938             this.maskEl.dom.style.display = 'block';
19939             this.maskEl.addClass('show');
19940         }
19941         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19942
19943         
19944         
19945         this.fireEvent('show', this);
19946         
19947     },
19948     hide : function()
19949     {
19950         this.el.setXY([0,0]);
19951         this.el.removeClass('in');
19952         this.el.hide();
19953         this.hoverState = null;
19954         this.maskEl.hide(); // always..
19955         this.fireEvent('hide', this);
19956     }
19957     
19958 });
19959
19960
19961 Roo.apply(Roo.bootstrap.Popover, {
19962
19963     alignment : {
19964         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19965         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19966         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19967         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19968     },
19969     
19970     zIndex : 20001,
19971
19972     clickHander : false,
19973     
19974
19975     onMouseDown : function(e)
19976     {
19977         if (!e.getTarget(".roo-popover")) {
19978             this.hideAll();
19979         }
19980          
19981     },
19982     
19983     popups : [],
19984     
19985     register : function(popup)
19986     {
19987         if (!Roo.bootstrap.Popover.clickHandler) {
19988             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19989         }
19990         // hide other popups.
19991         this.hideAll();
19992         this.popups.push(popup);
19993     },
19994     hideAll : function()
19995     {
19996         this.popups.forEach(function(p) {
19997             p.hide();
19998         });
19999     }
20000
20001 });/*
20002  * - LGPL
20003  *
20004  * Progress
20005  * 
20006  */
20007
20008 /**
20009  * @class Roo.bootstrap.Progress
20010  * @extends Roo.bootstrap.Component
20011  * Bootstrap Progress class
20012  * @cfg {Boolean} striped striped of the progress bar
20013  * @cfg {Boolean} active animated of the progress bar
20014  * 
20015  * 
20016  * @constructor
20017  * Create a new Progress
20018  * @param {Object} config The config object
20019  */
20020
20021 Roo.bootstrap.Progress = function(config){
20022     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20023 };
20024
20025 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20026     
20027     striped : false,
20028     active: false,
20029     
20030     getAutoCreate : function(){
20031         var cfg = {
20032             tag: 'div',
20033             cls: 'progress'
20034         };
20035         
20036         
20037         if(this.striped){
20038             cfg.cls += ' progress-striped';
20039         }
20040       
20041         if(this.active){
20042             cfg.cls += ' active';
20043         }
20044         
20045         
20046         return cfg;
20047     }
20048    
20049 });
20050
20051  
20052
20053  /*
20054  * - LGPL
20055  *
20056  * ProgressBar
20057  * 
20058  */
20059
20060 /**
20061  * @class Roo.bootstrap.ProgressBar
20062  * @extends Roo.bootstrap.Component
20063  * Bootstrap ProgressBar class
20064  * @cfg {Number} aria_valuenow aria-value now
20065  * @cfg {Number} aria_valuemin aria-value min
20066  * @cfg {Number} aria_valuemax aria-value max
20067  * @cfg {String} label label for the progress bar
20068  * @cfg {String} panel (success | info | warning | danger )
20069  * @cfg {String} role role of the progress bar
20070  * @cfg {String} sr_only text
20071  * 
20072  * 
20073  * @constructor
20074  * Create a new ProgressBar
20075  * @param {Object} config The config object
20076  */
20077
20078 Roo.bootstrap.ProgressBar = function(config){
20079     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20080 };
20081
20082 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20083     
20084     aria_valuenow : 0,
20085     aria_valuemin : 0,
20086     aria_valuemax : 100,
20087     label : false,
20088     panel : false,
20089     role : false,
20090     sr_only: false,
20091     
20092     getAutoCreate : function()
20093     {
20094         
20095         var cfg = {
20096             tag: 'div',
20097             cls: 'progress-bar',
20098             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20099         };
20100         
20101         if(this.sr_only){
20102             cfg.cn = {
20103                 tag: 'span',
20104                 cls: 'sr-only',
20105                 html: this.sr_only
20106             }
20107         }
20108         
20109         if(this.role){
20110             cfg.role = this.role;
20111         }
20112         
20113         if(this.aria_valuenow){
20114             cfg['aria-valuenow'] = this.aria_valuenow;
20115         }
20116         
20117         if(this.aria_valuemin){
20118             cfg['aria-valuemin'] = this.aria_valuemin;
20119         }
20120         
20121         if(this.aria_valuemax){
20122             cfg['aria-valuemax'] = this.aria_valuemax;
20123         }
20124         
20125         if(this.label && !this.sr_only){
20126             cfg.html = this.label;
20127         }
20128         
20129         if(this.panel){
20130             cfg.cls += ' progress-bar-' + this.panel;
20131         }
20132         
20133         return cfg;
20134     },
20135     
20136     update : function(aria_valuenow)
20137     {
20138         this.aria_valuenow = aria_valuenow;
20139         
20140         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20141     }
20142    
20143 });
20144
20145  
20146
20147  /*
20148  * - LGPL
20149  *
20150  * column
20151  * 
20152  */
20153
20154 /**
20155  * @class Roo.bootstrap.TabGroup
20156  * @extends Roo.bootstrap.Column
20157  * Bootstrap Column class
20158  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20159  * @cfg {Boolean} carousel true to make the group behave like a carousel
20160  * @cfg {Boolean} bullets show bullets for the panels
20161  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20162  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20163  * @cfg {Boolean} showarrow (true|false) show arrow default true
20164  * 
20165  * @constructor
20166  * Create a new TabGroup
20167  * @param {Object} config The config object
20168  */
20169
20170 Roo.bootstrap.TabGroup = function(config){
20171     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20172     if (!this.navId) {
20173         this.navId = Roo.id();
20174     }
20175     this.tabs = [];
20176     Roo.bootstrap.TabGroup.register(this);
20177     
20178 };
20179
20180 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20181     
20182     carousel : false,
20183     transition : false,
20184     bullets : 0,
20185     timer : 0,
20186     autoslide : false,
20187     slideFn : false,
20188     slideOnTouch : false,
20189     showarrow : true,
20190     
20191     getAutoCreate : function()
20192     {
20193         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20194         
20195         cfg.cls += ' tab-content';
20196         
20197         if (this.carousel) {
20198             cfg.cls += ' carousel slide';
20199             
20200             cfg.cn = [{
20201                cls : 'carousel-inner',
20202                cn : []
20203             }];
20204         
20205             if(this.bullets  && !Roo.isTouch){
20206                 
20207                 var bullets = {
20208                     cls : 'carousel-bullets',
20209                     cn : []
20210                 };
20211                
20212                 if(this.bullets_cls){
20213                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20214                 }
20215                 
20216                 bullets.cn.push({
20217                     cls : 'clear'
20218                 });
20219                 
20220                 cfg.cn[0].cn.push(bullets);
20221             }
20222             
20223             if(this.showarrow){
20224                 cfg.cn[0].cn.push({
20225                     tag : 'div',
20226                     class : 'carousel-arrow',
20227                     cn : [
20228                         {
20229                             tag : 'div',
20230                             class : 'carousel-prev',
20231                             cn : [
20232                                 {
20233                                     tag : 'i',
20234                                     class : 'fa fa-chevron-left'
20235                                 }
20236                             ]
20237                         },
20238                         {
20239                             tag : 'div',
20240                             class : 'carousel-next',
20241                             cn : [
20242                                 {
20243                                     tag : 'i',
20244                                     class : 'fa fa-chevron-right'
20245                                 }
20246                             ]
20247                         }
20248                     ]
20249                 });
20250             }
20251             
20252         }
20253         
20254         return cfg;
20255     },
20256     
20257     initEvents:  function()
20258     {
20259 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20260 //            this.el.on("touchstart", this.onTouchStart, this);
20261 //        }
20262         
20263         if(this.autoslide){
20264             var _this = this;
20265             
20266             this.slideFn = window.setInterval(function() {
20267                 _this.showPanelNext();
20268             }, this.timer);
20269         }
20270         
20271         if(this.showarrow){
20272             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20273             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20274         }
20275         
20276         
20277     },
20278     
20279 //    onTouchStart : function(e, el, o)
20280 //    {
20281 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20282 //            return;
20283 //        }
20284 //        
20285 //        this.showPanelNext();
20286 //    },
20287     
20288     
20289     getChildContainer : function()
20290     {
20291         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20292     },
20293     
20294     /**
20295     * register a Navigation item
20296     * @param {Roo.bootstrap.NavItem} the navitem to add
20297     */
20298     register : function(item)
20299     {
20300         this.tabs.push( item);
20301         item.navId = this.navId; // not really needed..
20302         this.addBullet();
20303     
20304     },
20305     
20306     getActivePanel : function()
20307     {
20308         var r = false;
20309         Roo.each(this.tabs, function(t) {
20310             if (t.active) {
20311                 r = t;
20312                 return false;
20313             }
20314             return null;
20315         });
20316         return r;
20317         
20318     },
20319     getPanelByName : function(n)
20320     {
20321         var r = false;
20322         Roo.each(this.tabs, function(t) {
20323             if (t.tabId == n) {
20324                 r = t;
20325                 return false;
20326             }
20327             return null;
20328         });
20329         return r;
20330     },
20331     indexOfPanel : function(p)
20332     {
20333         var r = false;
20334         Roo.each(this.tabs, function(t,i) {
20335             if (t.tabId == p.tabId) {
20336                 r = i;
20337                 return false;
20338             }
20339             return null;
20340         });
20341         return r;
20342     },
20343     /**
20344      * show a specific panel
20345      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20346      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20347      */
20348     showPanel : function (pan)
20349     {
20350         if(this.transition || typeof(pan) == 'undefined'){
20351             Roo.log("waiting for the transitionend");
20352             return false;
20353         }
20354         
20355         if (typeof(pan) == 'number') {
20356             pan = this.tabs[pan];
20357         }
20358         
20359         if (typeof(pan) == 'string') {
20360             pan = this.getPanelByName(pan);
20361         }
20362         
20363         var cur = this.getActivePanel();
20364         
20365         if(!pan || !cur){
20366             Roo.log('pan or acitve pan is undefined');
20367             return false;
20368         }
20369         
20370         if (pan.tabId == this.getActivePanel().tabId) {
20371             return true;
20372         }
20373         
20374         if (false === cur.fireEvent('beforedeactivate')) {
20375             return false;
20376         }
20377         
20378         if(this.bullets > 0 && !Roo.isTouch){
20379             this.setActiveBullet(this.indexOfPanel(pan));
20380         }
20381         
20382         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20383             
20384             //class="carousel-item carousel-item-next carousel-item-left"
20385             
20386             this.transition = true;
20387             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20388             var lr = dir == 'next' ? 'left' : 'right';
20389             pan.el.addClass(dir); // or prev
20390             pan.el.addClass('carousel-item-' + dir); // or prev
20391             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20392             cur.el.addClass(lr); // or right
20393             pan.el.addClass(lr);
20394             cur.el.addClass('carousel-item-' +lr); // or right
20395             pan.el.addClass('carousel-item-' +lr);
20396             
20397             
20398             var _this = this;
20399             cur.el.on('transitionend', function() {
20400                 Roo.log("trans end?");
20401                 
20402                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20403                 pan.setActive(true);
20404                 
20405                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20406                 cur.setActive(false);
20407                 
20408                 _this.transition = false;
20409                 
20410             }, this, { single:  true } );
20411             
20412             return true;
20413         }
20414         
20415         cur.setActive(false);
20416         pan.setActive(true);
20417         
20418         return true;
20419         
20420     },
20421     showPanelNext : function()
20422     {
20423         var i = this.indexOfPanel(this.getActivePanel());
20424         
20425         if (i >= this.tabs.length - 1 && !this.autoslide) {
20426             return;
20427         }
20428         
20429         if (i >= this.tabs.length - 1 && this.autoslide) {
20430             i = -1;
20431         }
20432         
20433         this.showPanel(this.tabs[i+1]);
20434     },
20435     
20436     showPanelPrev : function()
20437     {
20438         var i = this.indexOfPanel(this.getActivePanel());
20439         
20440         if (i  < 1 && !this.autoslide) {
20441             return;
20442         }
20443         
20444         if (i < 1 && this.autoslide) {
20445             i = this.tabs.length;
20446         }
20447         
20448         this.showPanel(this.tabs[i-1]);
20449     },
20450     
20451     
20452     addBullet: function()
20453     {
20454         if(!this.bullets || Roo.isTouch){
20455             return;
20456         }
20457         var ctr = this.el.select('.carousel-bullets',true).first();
20458         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20459         var bullet = ctr.createChild({
20460             cls : 'bullet bullet-' + i
20461         },ctr.dom.lastChild);
20462         
20463         
20464         var _this = this;
20465         
20466         bullet.on('click', (function(e, el, o, ii, t){
20467
20468             e.preventDefault();
20469
20470             this.showPanel(ii);
20471
20472             if(this.autoslide && this.slideFn){
20473                 clearInterval(this.slideFn);
20474                 this.slideFn = window.setInterval(function() {
20475                     _this.showPanelNext();
20476                 }, this.timer);
20477             }
20478
20479         }).createDelegate(this, [i, bullet], true));
20480                 
20481         
20482     },
20483      
20484     setActiveBullet : function(i)
20485     {
20486         if(Roo.isTouch){
20487             return;
20488         }
20489         
20490         Roo.each(this.el.select('.bullet', true).elements, function(el){
20491             el.removeClass('selected');
20492         });
20493
20494         var bullet = this.el.select('.bullet-' + i, true).first();
20495         
20496         if(!bullet){
20497             return;
20498         }
20499         
20500         bullet.addClass('selected');
20501     }
20502     
20503     
20504   
20505 });
20506
20507  
20508
20509  
20510  
20511 Roo.apply(Roo.bootstrap.TabGroup, {
20512     
20513     groups: {},
20514      /**
20515     * register a Navigation Group
20516     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20517     */
20518     register : function(navgrp)
20519     {
20520         this.groups[navgrp.navId] = navgrp;
20521         
20522     },
20523     /**
20524     * fetch a Navigation Group based on the navigation ID
20525     * if one does not exist , it will get created.
20526     * @param {string} the navgroup to add
20527     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20528     */
20529     get: function(navId) {
20530         if (typeof(this.groups[navId]) == 'undefined') {
20531             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20532         }
20533         return this.groups[navId] ;
20534     }
20535     
20536     
20537     
20538 });
20539
20540  /*
20541  * - LGPL
20542  *
20543  * TabPanel
20544  * 
20545  */
20546
20547 /**
20548  * @class Roo.bootstrap.TabPanel
20549  * @extends Roo.bootstrap.Component
20550  * Bootstrap TabPanel class
20551  * @cfg {Boolean} active panel active
20552  * @cfg {String} html panel content
20553  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20554  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20555  * @cfg {String} href click to link..
20556  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20557  * 
20558  * 
20559  * @constructor
20560  * Create a new TabPanel
20561  * @param {Object} config The config object
20562  */
20563
20564 Roo.bootstrap.TabPanel = function(config){
20565     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20566     this.addEvents({
20567         /**
20568              * @event changed
20569              * Fires when the active status changes
20570              * @param {Roo.bootstrap.TabPanel} this
20571              * @param {Boolean} state the new state
20572             
20573          */
20574         'changed': true,
20575         /**
20576              * @event beforedeactivate
20577              * Fires before a tab is de-activated - can be used to do validation on a form.
20578              * @param {Roo.bootstrap.TabPanel} this
20579              * @return {Boolean} false if there is an error
20580             
20581          */
20582         'beforedeactivate': true
20583      });
20584     
20585     this.tabId = this.tabId || Roo.id();
20586   
20587 };
20588
20589 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20590     
20591     active: false,
20592     html: false,
20593     tabId: false,
20594     navId : false,
20595     href : '',
20596     touchSlide : false,
20597     getAutoCreate : function(){
20598         
20599         
20600         var cfg = {
20601             tag: 'div',
20602             // item is needed for carousel - not sure if it has any effect otherwise
20603             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20604             html: this.html || ''
20605         };
20606         
20607         if(this.active){
20608             cfg.cls += ' active';
20609         }
20610         
20611         if(this.tabId){
20612             cfg.tabId = this.tabId;
20613         }
20614         
20615         
20616         
20617         return cfg;
20618     },
20619     
20620     initEvents:  function()
20621     {
20622         var p = this.parent();
20623         
20624         this.navId = this.navId || p.navId;
20625         
20626         if (typeof(this.navId) != 'undefined') {
20627             // not really needed.. but just in case.. parent should be a NavGroup.
20628             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20629             
20630             tg.register(this);
20631             
20632             var i = tg.tabs.length - 1;
20633             
20634             if(this.active && tg.bullets > 0 && i < tg.bullets){
20635                 tg.setActiveBullet(i);
20636             }
20637         }
20638         
20639         this.el.on('click', this.onClick, this);
20640         
20641         if(Roo.isTouch && this.touchSlide){
20642             this.el.on("touchstart", this.onTouchStart, this);
20643             this.el.on("touchmove", this.onTouchMove, this);
20644             this.el.on("touchend", this.onTouchEnd, this);
20645         }
20646         
20647     },
20648     
20649     onRender : function(ct, position)
20650     {
20651         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20652     },
20653     
20654     setActive : function(state)
20655     {
20656         Roo.log("panel - set active " + this.tabId + "=" + state);
20657         
20658         this.active = state;
20659         if (!state) {
20660             this.el.removeClass('active');
20661             
20662         } else  if (!this.el.hasClass('active')) {
20663             this.el.addClass('active');
20664         }
20665         
20666         this.fireEvent('changed', this, state);
20667     },
20668     
20669     onClick : function(e)
20670     {
20671         e.preventDefault();
20672         
20673         if(!this.href.length){
20674             return;
20675         }
20676         
20677         window.location.href = this.href;
20678     },
20679     
20680     startX : 0,
20681     startY : 0,
20682     endX : 0,
20683     endY : 0,
20684     swiping : false,
20685     
20686     onTouchStart : function(e)
20687     {
20688         this.swiping = false;
20689         
20690         this.startX = e.browserEvent.touches[0].clientX;
20691         this.startY = e.browserEvent.touches[0].clientY;
20692     },
20693     
20694     onTouchMove : function(e)
20695     {
20696         this.swiping = true;
20697         
20698         this.endX = e.browserEvent.touches[0].clientX;
20699         this.endY = e.browserEvent.touches[0].clientY;
20700     },
20701     
20702     onTouchEnd : function(e)
20703     {
20704         if(!this.swiping){
20705             this.onClick(e);
20706             return;
20707         }
20708         
20709         var tabGroup = this.parent();
20710         
20711         if(this.endX > this.startX){ // swiping right
20712             tabGroup.showPanelPrev();
20713             return;
20714         }
20715         
20716         if(this.startX > this.endX){ // swiping left
20717             tabGroup.showPanelNext();
20718             return;
20719         }
20720     }
20721     
20722     
20723 });
20724  
20725
20726  
20727
20728  /*
20729  * - LGPL
20730  *
20731  * DateField
20732  * 
20733  */
20734
20735 /**
20736  * @class Roo.bootstrap.DateField
20737  * @extends Roo.bootstrap.Input
20738  * Bootstrap DateField class
20739  * @cfg {Number} weekStart default 0
20740  * @cfg {String} viewMode default empty, (months|years)
20741  * @cfg {String} minViewMode default empty, (months|years)
20742  * @cfg {Number} startDate default -Infinity
20743  * @cfg {Number} endDate default Infinity
20744  * @cfg {Boolean} todayHighlight default false
20745  * @cfg {Boolean} todayBtn default false
20746  * @cfg {Boolean} calendarWeeks default false
20747  * @cfg {Object} daysOfWeekDisabled default empty
20748  * @cfg {Boolean} singleMode default false (true | false)
20749  * 
20750  * @cfg {Boolean} keyboardNavigation default true
20751  * @cfg {String} language default en
20752  * 
20753  * @constructor
20754  * Create a new DateField
20755  * @param {Object} config The config object
20756  */
20757
20758 Roo.bootstrap.DateField = function(config){
20759     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20760      this.addEvents({
20761             /**
20762              * @event show
20763              * Fires when this field show.
20764              * @param {Roo.bootstrap.DateField} this
20765              * @param {Mixed} date The date value
20766              */
20767             show : true,
20768             /**
20769              * @event show
20770              * Fires when this field hide.
20771              * @param {Roo.bootstrap.DateField} this
20772              * @param {Mixed} date The date value
20773              */
20774             hide : true,
20775             /**
20776              * @event select
20777              * Fires when select a date.
20778              * @param {Roo.bootstrap.DateField} this
20779              * @param {Mixed} date The date value
20780              */
20781             select : true,
20782             /**
20783              * @event beforeselect
20784              * Fires when before select a date.
20785              * @param {Roo.bootstrap.DateField} this
20786              * @param {Mixed} date The date value
20787              */
20788             beforeselect : true
20789         });
20790 };
20791
20792 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20793     
20794     /**
20795      * @cfg {String} format
20796      * The default date format string which can be overriden for localization support.  The format must be
20797      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20798      */
20799     format : "m/d/y",
20800     /**
20801      * @cfg {String} altFormats
20802      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20803      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20804      */
20805     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20806     
20807     weekStart : 0,
20808     
20809     viewMode : '',
20810     
20811     minViewMode : '',
20812     
20813     todayHighlight : false,
20814     
20815     todayBtn: false,
20816     
20817     language: 'en',
20818     
20819     keyboardNavigation: true,
20820     
20821     calendarWeeks: false,
20822     
20823     startDate: -Infinity,
20824     
20825     endDate: Infinity,
20826     
20827     daysOfWeekDisabled: [],
20828     
20829     _events: [],
20830     
20831     singleMode : false,
20832     
20833     UTCDate: function()
20834     {
20835         return new Date(Date.UTC.apply(Date, arguments));
20836     },
20837     
20838     UTCToday: function()
20839     {
20840         var today = new Date();
20841         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20842     },
20843     
20844     getDate: function() {
20845             var d = this.getUTCDate();
20846             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20847     },
20848     
20849     getUTCDate: function() {
20850             return this.date;
20851     },
20852     
20853     setDate: function(d) {
20854             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20855     },
20856     
20857     setUTCDate: function(d) {
20858             this.date = d;
20859             this.setValue(this.formatDate(this.date));
20860     },
20861         
20862     onRender: function(ct, position)
20863     {
20864         
20865         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20866         
20867         this.language = this.language || 'en';
20868         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20869         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20870         
20871         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20872         this.format = this.format || 'm/d/y';
20873         this.isInline = false;
20874         this.isInput = true;
20875         this.component = this.el.select('.add-on', true).first() || false;
20876         this.component = (this.component && this.component.length === 0) ? false : this.component;
20877         this.hasInput = this.component && this.inputEl().length;
20878         
20879         if (typeof(this.minViewMode === 'string')) {
20880             switch (this.minViewMode) {
20881                 case 'months':
20882                     this.minViewMode = 1;
20883                     break;
20884                 case 'years':
20885                     this.minViewMode = 2;
20886                     break;
20887                 default:
20888                     this.minViewMode = 0;
20889                     break;
20890             }
20891         }
20892         
20893         if (typeof(this.viewMode === 'string')) {
20894             switch (this.viewMode) {
20895                 case 'months':
20896                     this.viewMode = 1;
20897                     break;
20898                 case 'years':
20899                     this.viewMode = 2;
20900                     break;
20901                 default:
20902                     this.viewMode = 0;
20903                     break;
20904             }
20905         }
20906                 
20907         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20908         
20909 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20910         
20911         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20912         
20913         this.picker().on('mousedown', this.onMousedown, this);
20914         this.picker().on('click', this.onClick, this);
20915         
20916         this.picker().addClass('datepicker-dropdown');
20917         
20918         this.startViewMode = this.viewMode;
20919         
20920         if(this.singleMode){
20921             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20922                 v.setVisibilityMode(Roo.Element.DISPLAY);
20923                 v.hide();
20924             });
20925             
20926             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20927                 v.setStyle('width', '189px');
20928             });
20929         }
20930         
20931         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20932             if(!this.calendarWeeks){
20933                 v.remove();
20934                 return;
20935             }
20936             
20937             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20938             v.attr('colspan', function(i, val){
20939                 return parseInt(val) + 1;
20940             });
20941         });
20942                         
20943         
20944         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20945         
20946         this.setStartDate(this.startDate);
20947         this.setEndDate(this.endDate);
20948         
20949         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20950         
20951         this.fillDow();
20952         this.fillMonths();
20953         this.update();
20954         this.showMode();
20955         
20956         if(this.isInline) {
20957             this.showPopup();
20958         }
20959     },
20960     
20961     picker : function()
20962     {
20963         return this.pickerEl;
20964 //        return this.el.select('.datepicker', true).first();
20965     },
20966     
20967     fillDow: function()
20968     {
20969         var dowCnt = this.weekStart;
20970         
20971         var dow = {
20972             tag: 'tr',
20973             cn: [
20974                 
20975             ]
20976         };
20977         
20978         if(this.calendarWeeks){
20979             dow.cn.push({
20980                 tag: 'th',
20981                 cls: 'cw',
20982                 html: '&nbsp;'
20983             })
20984         }
20985         
20986         while (dowCnt < this.weekStart + 7) {
20987             dow.cn.push({
20988                 tag: 'th',
20989                 cls: 'dow',
20990                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20991             });
20992         }
20993         
20994         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20995     },
20996     
20997     fillMonths: function()
20998     {    
20999         var i = 0;
21000         var months = this.picker().select('>.datepicker-months td', true).first();
21001         
21002         months.dom.innerHTML = '';
21003         
21004         while (i < 12) {
21005             var month = {
21006                 tag: 'span',
21007                 cls: 'month',
21008                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21009             };
21010             
21011             months.createChild(month);
21012         }
21013         
21014     },
21015     
21016     update: function()
21017     {
21018         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;
21019         
21020         if (this.date < this.startDate) {
21021             this.viewDate = new Date(this.startDate);
21022         } else if (this.date > this.endDate) {
21023             this.viewDate = new Date(this.endDate);
21024         } else {
21025             this.viewDate = new Date(this.date);
21026         }
21027         
21028         this.fill();
21029     },
21030     
21031     fill: function() 
21032     {
21033         var d = new Date(this.viewDate),
21034                 year = d.getUTCFullYear(),
21035                 month = d.getUTCMonth(),
21036                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21037                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21038                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21039                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21040                 currentDate = this.date && this.date.valueOf(),
21041                 today = this.UTCToday();
21042         
21043         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21044         
21045 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21046         
21047 //        this.picker.select('>tfoot th.today').
21048 //                                              .text(dates[this.language].today)
21049 //                                              .toggle(this.todayBtn !== false);
21050     
21051         this.updateNavArrows();
21052         this.fillMonths();
21053                                                 
21054         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21055         
21056         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21057          
21058         prevMonth.setUTCDate(day);
21059         
21060         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21061         
21062         var nextMonth = new Date(prevMonth);
21063         
21064         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21065         
21066         nextMonth = nextMonth.valueOf();
21067         
21068         var fillMonths = false;
21069         
21070         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21071         
21072         while(prevMonth.valueOf() <= nextMonth) {
21073             var clsName = '';
21074             
21075             if (prevMonth.getUTCDay() === this.weekStart) {
21076                 if(fillMonths){
21077                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21078                 }
21079                     
21080                 fillMonths = {
21081                     tag: 'tr',
21082                     cn: []
21083                 };
21084                 
21085                 if(this.calendarWeeks){
21086                     // ISO 8601: First week contains first thursday.
21087                     // ISO also states week starts on Monday, but we can be more abstract here.
21088                     var
21089                     // Start of current week: based on weekstart/current date
21090                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21091                     // Thursday of this week
21092                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21093                     // First Thursday of year, year from thursday
21094                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21095                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21096                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21097                     
21098                     fillMonths.cn.push({
21099                         tag: 'td',
21100                         cls: 'cw',
21101                         html: calWeek
21102                     });
21103                 }
21104             }
21105             
21106             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21107                 clsName += ' old';
21108             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21109                 clsName += ' new';
21110             }
21111             if (this.todayHighlight &&
21112                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21113                 prevMonth.getUTCMonth() == today.getMonth() &&
21114                 prevMonth.getUTCDate() == today.getDate()) {
21115                 clsName += ' today';
21116             }
21117             
21118             if (currentDate && prevMonth.valueOf() === currentDate) {
21119                 clsName += ' active';
21120             }
21121             
21122             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21123                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21124                     clsName += ' disabled';
21125             }
21126             
21127             fillMonths.cn.push({
21128                 tag: 'td',
21129                 cls: 'day ' + clsName,
21130                 html: prevMonth.getDate()
21131             });
21132             
21133             prevMonth.setDate(prevMonth.getDate()+1);
21134         }
21135           
21136         var currentYear = this.date && this.date.getUTCFullYear();
21137         var currentMonth = this.date && this.date.getUTCMonth();
21138         
21139         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21140         
21141         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21142             v.removeClass('active');
21143             
21144             if(currentYear === year && k === currentMonth){
21145                 v.addClass('active');
21146             }
21147             
21148             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21149                 v.addClass('disabled');
21150             }
21151             
21152         });
21153         
21154         
21155         year = parseInt(year/10, 10) * 10;
21156         
21157         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21158         
21159         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21160         
21161         year -= 1;
21162         for (var i = -1; i < 11; i++) {
21163             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21164                 tag: 'span',
21165                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21166                 html: year
21167             });
21168             
21169             year += 1;
21170         }
21171     },
21172     
21173     showMode: function(dir) 
21174     {
21175         if (dir) {
21176             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21177         }
21178         
21179         Roo.each(this.picker().select('>div',true).elements, function(v){
21180             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21181             v.hide();
21182         });
21183         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21184     },
21185     
21186     place: function()
21187     {
21188         if(this.isInline) {
21189             return;
21190         }
21191         
21192         this.picker().removeClass(['bottom', 'top']);
21193         
21194         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21195             /*
21196              * place to the top of element!
21197              *
21198              */
21199             
21200             this.picker().addClass('top');
21201             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21202             
21203             return;
21204         }
21205         
21206         this.picker().addClass('bottom');
21207         
21208         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21209     },
21210     
21211     parseDate : function(value)
21212     {
21213         if(!value || value instanceof Date){
21214             return value;
21215         }
21216         var v = Date.parseDate(value, this.format);
21217         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21218             v = Date.parseDate(value, 'Y-m-d');
21219         }
21220         if(!v && this.altFormats){
21221             if(!this.altFormatsArray){
21222                 this.altFormatsArray = this.altFormats.split("|");
21223             }
21224             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21225                 v = Date.parseDate(value, this.altFormatsArray[i]);
21226             }
21227         }
21228         return v;
21229     },
21230     
21231     formatDate : function(date, fmt)
21232     {   
21233         return (!date || !(date instanceof Date)) ?
21234         date : date.dateFormat(fmt || this.format);
21235     },
21236     
21237     onFocus : function()
21238     {
21239         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21240         this.showPopup();
21241     },
21242     
21243     onBlur : function()
21244     {
21245         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21246         
21247         var d = this.inputEl().getValue();
21248         
21249         this.setValue(d);
21250                 
21251         this.hidePopup();
21252     },
21253     
21254     showPopup : function()
21255     {
21256         this.picker().show();
21257         this.update();
21258         this.place();
21259         
21260         this.fireEvent('showpopup', this, this.date);
21261     },
21262     
21263     hidePopup : function()
21264     {
21265         if(this.isInline) {
21266             return;
21267         }
21268         this.picker().hide();
21269         this.viewMode = this.startViewMode;
21270         this.showMode();
21271         
21272         this.fireEvent('hidepopup', this, this.date);
21273         
21274     },
21275     
21276     onMousedown: function(e)
21277     {
21278         e.stopPropagation();
21279         e.preventDefault();
21280     },
21281     
21282     keyup: function(e)
21283     {
21284         Roo.bootstrap.DateField.superclass.keyup.call(this);
21285         this.update();
21286     },
21287
21288     setValue: function(v)
21289     {
21290         if(this.fireEvent('beforeselect', this, v) !== false){
21291             var d = new Date(this.parseDate(v) ).clearTime();
21292         
21293             if(isNaN(d.getTime())){
21294                 this.date = this.viewDate = '';
21295                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21296                 return;
21297             }
21298
21299             v = this.formatDate(d);
21300
21301             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21302
21303             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21304
21305             this.update();
21306
21307             this.fireEvent('select', this, this.date);
21308         }
21309     },
21310     
21311     getValue: function()
21312     {
21313         return this.formatDate(this.date);
21314     },
21315     
21316     fireKey: function(e)
21317     {
21318         if (!this.picker().isVisible()){
21319             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21320                 this.showPopup();
21321             }
21322             return;
21323         }
21324         
21325         var dateChanged = false,
21326         dir, day, month,
21327         newDate, newViewDate;
21328         
21329         switch(e.keyCode){
21330             case 27: // escape
21331                 this.hidePopup();
21332                 e.preventDefault();
21333                 break;
21334             case 37: // left
21335             case 39: // right
21336                 if (!this.keyboardNavigation) {
21337                     break;
21338                 }
21339                 dir = e.keyCode == 37 ? -1 : 1;
21340                 
21341                 if (e.ctrlKey){
21342                     newDate = this.moveYear(this.date, dir);
21343                     newViewDate = this.moveYear(this.viewDate, dir);
21344                 } else if (e.shiftKey){
21345                     newDate = this.moveMonth(this.date, dir);
21346                     newViewDate = this.moveMonth(this.viewDate, dir);
21347                 } else {
21348                     newDate = new Date(this.date);
21349                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21350                     newViewDate = new Date(this.viewDate);
21351                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21352                 }
21353                 if (this.dateWithinRange(newDate)){
21354                     this.date = newDate;
21355                     this.viewDate = newViewDate;
21356                     this.setValue(this.formatDate(this.date));
21357 //                    this.update();
21358                     e.preventDefault();
21359                     dateChanged = true;
21360                 }
21361                 break;
21362             case 38: // up
21363             case 40: // down
21364                 if (!this.keyboardNavigation) {
21365                     break;
21366                 }
21367                 dir = e.keyCode == 38 ? -1 : 1;
21368                 if (e.ctrlKey){
21369                     newDate = this.moveYear(this.date, dir);
21370                     newViewDate = this.moveYear(this.viewDate, dir);
21371                 } else if (e.shiftKey){
21372                     newDate = this.moveMonth(this.date, dir);
21373                     newViewDate = this.moveMonth(this.viewDate, dir);
21374                 } else {
21375                     newDate = new Date(this.date);
21376                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21377                     newViewDate = new Date(this.viewDate);
21378                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21379                 }
21380                 if (this.dateWithinRange(newDate)){
21381                     this.date = newDate;
21382                     this.viewDate = newViewDate;
21383                     this.setValue(this.formatDate(this.date));
21384 //                    this.update();
21385                     e.preventDefault();
21386                     dateChanged = true;
21387                 }
21388                 break;
21389             case 13: // enter
21390                 this.setValue(this.formatDate(this.date));
21391                 this.hidePopup();
21392                 e.preventDefault();
21393                 break;
21394             case 9: // tab
21395                 this.setValue(this.formatDate(this.date));
21396                 this.hidePopup();
21397                 break;
21398             case 16: // shift
21399             case 17: // ctrl
21400             case 18: // alt
21401                 break;
21402             default :
21403                 this.hidePopup();
21404                 
21405         }
21406     },
21407     
21408     
21409     onClick: function(e) 
21410     {
21411         e.stopPropagation();
21412         e.preventDefault();
21413         
21414         var target = e.getTarget();
21415         
21416         if(target.nodeName.toLowerCase() === 'i'){
21417             target = Roo.get(target).dom.parentNode;
21418         }
21419         
21420         var nodeName = target.nodeName;
21421         var className = target.className;
21422         var html = target.innerHTML;
21423         //Roo.log(nodeName);
21424         
21425         switch(nodeName.toLowerCase()) {
21426             case 'th':
21427                 switch(className) {
21428                     case 'switch':
21429                         this.showMode(1);
21430                         break;
21431                     case 'prev':
21432                     case 'next':
21433                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21434                         switch(this.viewMode){
21435                                 case 0:
21436                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21437                                         break;
21438                                 case 1:
21439                                 case 2:
21440                                         this.viewDate = this.moveYear(this.viewDate, dir);
21441                                         break;
21442                         }
21443                         this.fill();
21444                         break;
21445                     case 'today':
21446                         var date = new Date();
21447                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21448 //                        this.fill()
21449                         this.setValue(this.formatDate(this.date));
21450                         
21451                         this.hidePopup();
21452                         break;
21453                 }
21454                 break;
21455             case 'span':
21456                 if (className.indexOf('disabled') < 0) {
21457                     this.viewDate.setUTCDate(1);
21458                     if (className.indexOf('month') > -1) {
21459                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21460                     } else {
21461                         var year = parseInt(html, 10) || 0;
21462                         this.viewDate.setUTCFullYear(year);
21463                         
21464                     }
21465                     
21466                     if(this.singleMode){
21467                         this.setValue(this.formatDate(this.viewDate));
21468                         this.hidePopup();
21469                         return;
21470                     }
21471                     
21472                     this.showMode(-1);
21473                     this.fill();
21474                 }
21475                 break;
21476                 
21477             case 'td':
21478                 //Roo.log(className);
21479                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21480                     var day = parseInt(html, 10) || 1;
21481                     var year = this.viewDate.getUTCFullYear(),
21482                         month = this.viewDate.getUTCMonth();
21483
21484                     if (className.indexOf('old') > -1) {
21485                         if(month === 0 ){
21486                             month = 11;
21487                             year -= 1;
21488                         }else{
21489                             month -= 1;
21490                         }
21491                     } else if (className.indexOf('new') > -1) {
21492                         if (month == 11) {
21493                             month = 0;
21494                             year += 1;
21495                         } else {
21496                             month += 1;
21497                         }
21498                     }
21499                     //Roo.log([year,month,day]);
21500                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21501                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21502 //                    this.fill();
21503                     //Roo.log(this.formatDate(this.date));
21504                     this.setValue(this.formatDate(this.date));
21505                     this.hidePopup();
21506                 }
21507                 break;
21508         }
21509     },
21510     
21511     setStartDate: function(startDate)
21512     {
21513         this.startDate = startDate || -Infinity;
21514         if (this.startDate !== -Infinity) {
21515             this.startDate = this.parseDate(this.startDate);
21516         }
21517         this.update();
21518         this.updateNavArrows();
21519     },
21520
21521     setEndDate: function(endDate)
21522     {
21523         this.endDate = endDate || Infinity;
21524         if (this.endDate !== Infinity) {
21525             this.endDate = this.parseDate(this.endDate);
21526         }
21527         this.update();
21528         this.updateNavArrows();
21529     },
21530     
21531     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21532     {
21533         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21534         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21535             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21536         }
21537         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21538             return parseInt(d, 10);
21539         });
21540         this.update();
21541         this.updateNavArrows();
21542     },
21543     
21544     updateNavArrows: function() 
21545     {
21546         if(this.singleMode){
21547             return;
21548         }
21549         
21550         var d = new Date(this.viewDate),
21551         year = d.getUTCFullYear(),
21552         month = d.getUTCMonth();
21553         
21554         Roo.each(this.picker().select('.prev', true).elements, function(v){
21555             v.show();
21556             switch (this.viewMode) {
21557                 case 0:
21558
21559                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21560                         v.hide();
21561                     }
21562                     break;
21563                 case 1:
21564                 case 2:
21565                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21566                         v.hide();
21567                     }
21568                     break;
21569             }
21570         });
21571         
21572         Roo.each(this.picker().select('.next', true).elements, function(v){
21573             v.show();
21574             switch (this.viewMode) {
21575                 case 0:
21576
21577                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21578                         v.hide();
21579                     }
21580                     break;
21581                 case 1:
21582                 case 2:
21583                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21584                         v.hide();
21585                     }
21586                     break;
21587             }
21588         })
21589     },
21590     
21591     moveMonth: function(date, dir)
21592     {
21593         if (!dir) {
21594             return date;
21595         }
21596         var new_date = new Date(date.valueOf()),
21597         day = new_date.getUTCDate(),
21598         month = new_date.getUTCMonth(),
21599         mag = Math.abs(dir),
21600         new_month, test;
21601         dir = dir > 0 ? 1 : -1;
21602         if (mag == 1){
21603             test = dir == -1
21604             // If going back one month, make sure month is not current month
21605             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21606             ? function(){
21607                 return new_date.getUTCMonth() == month;
21608             }
21609             // If going forward one month, make sure month is as expected
21610             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21611             : function(){
21612                 return new_date.getUTCMonth() != new_month;
21613             };
21614             new_month = month + dir;
21615             new_date.setUTCMonth(new_month);
21616             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21617             if (new_month < 0 || new_month > 11) {
21618                 new_month = (new_month + 12) % 12;
21619             }
21620         } else {
21621             // For magnitudes >1, move one month at a time...
21622             for (var i=0; i<mag; i++) {
21623                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21624                 new_date = this.moveMonth(new_date, dir);
21625             }
21626             // ...then reset the day, keeping it in the new month
21627             new_month = new_date.getUTCMonth();
21628             new_date.setUTCDate(day);
21629             test = function(){
21630                 return new_month != new_date.getUTCMonth();
21631             };
21632         }
21633         // Common date-resetting loop -- if date is beyond end of month, make it
21634         // end of month
21635         while (test()){
21636             new_date.setUTCDate(--day);
21637             new_date.setUTCMonth(new_month);
21638         }
21639         return new_date;
21640     },
21641
21642     moveYear: function(date, dir)
21643     {
21644         return this.moveMonth(date, dir*12);
21645     },
21646
21647     dateWithinRange: function(date)
21648     {
21649         return date >= this.startDate && date <= this.endDate;
21650     },
21651
21652     
21653     remove: function() 
21654     {
21655         this.picker().remove();
21656     },
21657     
21658     validateValue : function(value)
21659     {
21660         if(this.getVisibilityEl().hasClass('hidden')){
21661             return true;
21662         }
21663         
21664         if(value.length < 1)  {
21665             if(this.allowBlank){
21666                 return true;
21667             }
21668             return false;
21669         }
21670         
21671         if(value.length < this.minLength){
21672             return false;
21673         }
21674         if(value.length > this.maxLength){
21675             return false;
21676         }
21677         if(this.vtype){
21678             var vt = Roo.form.VTypes;
21679             if(!vt[this.vtype](value, this)){
21680                 return false;
21681             }
21682         }
21683         if(typeof this.validator == "function"){
21684             var msg = this.validator(value);
21685             if(msg !== true){
21686                 return false;
21687             }
21688         }
21689         
21690         if(this.regex && !this.regex.test(value)){
21691             return false;
21692         }
21693         
21694         if(typeof(this.parseDate(value)) == 'undefined'){
21695             return false;
21696         }
21697         
21698         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21699             return false;
21700         }      
21701         
21702         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21703             return false;
21704         } 
21705         
21706         
21707         return true;
21708     },
21709     
21710     reset : function()
21711     {
21712         this.date = this.viewDate = '';
21713         
21714         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21715     }
21716    
21717 });
21718
21719 Roo.apply(Roo.bootstrap.DateField,  {
21720     
21721     head : {
21722         tag: 'thead',
21723         cn: [
21724         {
21725             tag: 'tr',
21726             cn: [
21727             {
21728                 tag: 'th',
21729                 cls: 'prev',
21730                 html: '<i class="fa fa-arrow-left"/>'
21731             },
21732             {
21733                 tag: 'th',
21734                 cls: 'switch',
21735                 colspan: '5'
21736             },
21737             {
21738                 tag: 'th',
21739                 cls: 'next',
21740                 html: '<i class="fa fa-arrow-right"/>'
21741             }
21742
21743             ]
21744         }
21745         ]
21746     },
21747     
21748     content : {
21749         tag: 'tbody',
21750         cn: [
21751         {
21752             tag: 'tr',
21753             cn: [
21754             {
21755                 tag: 'td',
21756                 colspan: '7'
21757             }
21758             ]
21759         }
21760         ]
21761     },
21762     
21763     footer : {
21764         tag: 'tfoot',
21765         cn: [
21766         {
21767             tag: 'tr',
21768             cn: [
21769             {
21770                 tag: 'th',
21771                 colspan: '7',
21772                 cls: 'today'
21773             }
21774                     
21775             ]
21776         }
21777         ]
21778     },
21779     
21780     dates:{
21781         en: {
21782             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21783             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21784             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21785             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21786             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21787             today: "Today"
21788         }
21789     },
21790     
21791     modes: [
21792     {
21793         clsName: 'days',
21794         navFnc: 'Month',
21795         navStep: 1
21796     },
21797     {
21798         clsName: 'months',
21799         navFnc: 'FullYear',
21800         navStep: 1
21801     },
21802     {
21803         clsName: 'years',
21804         navFnc: 'FullYear',
21805         navStep: 10
21806     }]
21807 });
21808
21809 Roo.apply(Roo.bootstrap.DateField,  {
21810   
21811     template : {
21812         tag: 'div',
21813         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21814         cn: [
21815         {
21816             tag: 'div',
21817             cls: 'datepicker-days',
21818             cn: [
21819             {
21820                 tag: 'table',
21821                 cls: 'table-condensed',
21822                 cn:[
21823                 Roo.bootstrap.DateField.head,
21824                 {
21825                     tag: 'tbody'
21826                 },
21827                 Roo.bootstrap.DateField.footer
21828                 ]
21829             }
21830             ]
21831         },
21832         {
21833             tag: 'div',
21834             cls: 'datepicker-months',
21835             cn: [
21836             {
21837                 tag: 'table',
21838                 cls: 'table-condensed',
21839                 cn:[
21840                 Roo.bootstrap.DateField.head,
21841                 Roo.bootstrap.DateField.content,
21842                 Roo.bootstrap.DateField.footer
21843                 ]
21844             }
21845             ]
21846         },
21847         {
21848             tag: 'div',
21849             cls: 'datepicker-years',
21850             cn: [
21851             {
21852                 tag: 'table',
21853                 cls: 'table-condensed',
21854                 cn:[
21855                 Roo.bootstrap.DateField.head,
21856                 Roo.bootstrap.DateField.content,
21857                 Roo.bootstrap.DateField.footer
21858                 ]
21859             }
21860             ]
21861         }
21862         ]
21863     }
21864 });
21865
21866  
21867
21868  /*
21869  * - LGPL
21870  *
21871  * TimeField
21872  * 
21873  */
21874
21875 /**
21876  * @class Roo.bootstrap.TimeField
21877  * @extends Roo.bootstrap.Input
21878  * Bootstrap DateField class
21879  * 
21880  * 
21881  * @constructor
21882  * Create a new TimeField
21883  * @param {Object} config The config object
21884  */
21885
21886 Roo.bootstrap.TimeField = function(config){
21887     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21888     this.addEvents({
21889             /**
21890              * @event show
21891              * Fires when this field show.
21892              * @param {Roo.bootstrap.DateField} thisthis
21893              * @param {Mixed} date The date value
21894              */
21895             show : true,
21896             /**
21897              * @event show
21898              * Fires when this field hide.
21899              * @param {Roo.bootstrap.DateField} this
21900              * @param {Mixed} date The date value
21901              */
21902             hide : true,
21903             /**
21904              * @event select
21905              * Fires when select a date.
21906              * @param {Roo.bootstrap.DateField} this
21907              * @param {Mixed} date The date value
21908              */
21909             select : true
21910         });
21911 };
21912
21913 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21914     
21915     /**
21916      * @cfg {String} format
21917      * The default time format string which can be overriden for localization support.  The format must be
21918      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21919      */
21920     format : "H:i",
21921        
21922     onRender: function(ct, position)
21923     {
21924         
21925         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21926                 
21927         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21928         
21929         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21930         
21931         this.pop = this.picker().select('>.datepicker-time',true).first();
21932         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21933         
21934         this.picker().on('mousedown', this.onMousedown, this);
21935         this.picker().on('click', this.onClick, this);
21936         
21937         this.picker().addClass('datepicker-dropdown');
21938     
21939         this.fillTime();
21940         this.update();
21941             
21942         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21943         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21944         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21945         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21946         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21947         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21948
21949     },
21950     
21951     fireKey: function(e){
21952         if (!this.picker().isVisible()){
21953             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21954                 this.show();
21955             }
21956             return;
21957         }
21958
21959         e.preventDefault();
21960         
21961         switch(e.keyCode){
21962             case 27: // escape
21963                 this.hide();
21964                 break;
21965             case 37: // left
21966             case 39: // right
21967                 this.onTogglePeriod();
21968                 break;
21969             case 38: // up
21970                 this.onIncrementMinutes();
21971                 break;
21972             case 40: // down
21973                 this.onDecrementMinutes();
21974                 break;
21975             case 13: // enter
21976             case 9: // tab
21977                 this.setTime();
21978                 break;
21979         }
21980     },
21981     
21982     onClick: function(e) {
21983         e.stopPropagation();
21984         e.preventDefault();
21985     },
21986     
21987     picker : function()
21988     {
21989         return this.el.select('.datepicker', true).first();
21990     },
21991     
21992     fillTime: function()
21993     {    
21994         var time = this.pop.select('tbody', true).first();
21995         
21996         time.dom.innerHTML = '';
21997         
21998         time.createChild({
21999             tag: 'tr',
22000             cn: [
22001                 {
22002                     tag: 'td',
22003                     cn: [
22004                         {
22005                             tag: 'a',
22006                             href: '#',
22007                             cls: 'btn',
22008                             cn: [
22009                                 {
22010                                     tag: 'span',
22011                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
22012                                 }
22013                             ]
22014                         } 
22015                     ]
22016                 },
22017                 {
22018                     tag: 'td',
22019                     cls: 'separator'
22020                 },
22021                 {
22022                     tag: 'td',
22023                     cn: [
22024                         {
22025                             tag: 'a',
22026                             href: '#',
22027                             cls: 'btn',
22028                             cn: [
22029                                 {
22030                                     tag: 'span',
22031                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22032                                 }
22033                             ]
22034                         }
22035                     ]
22036                 },
22037                 {
22038                     tag: 'td',
22039                     cls: 'separator'
22040                 }
22041             ]
22042         });
22043         
22044         time.createChild({
22045             tag: 'tr',
22046             cn: [
22047                 {
22048                     tag: 'td',
22049                     cn: [
22050                         {
22051                             tag: 'span',
22052                             cls: 'timepicker-hour',
22053                             html: '00'
22054                         }  
22055                     ]
22056                 },
22057                 {
22058                     tag: 'td',
22059                     cls: 'separator',
22060                     html: ':'
22061                 },
22062                 {
22063                     tag: 'td',
22064                     cn: [
22065                         {
22066                             tag: 'span',
22067                             cls: 'timepicker-minute',
22068                             html: '00'
22069                         }  
22070                     ]
22071                 },
22072                 {
22073                     tag: 'td',
22074                     cls: 'separator'
22075                 },
22076                 {
22077                     tag: 'td',
22078                     cn: [
22079                         {
22080                             tag: 'button',
22081                             type: 'button',
22082                             cls: 'btn btn-primary period',
22083                             html: 'AM'
22084                             
22085                         }
22086                     ]
22087                 }
22088             ]
22089         });
22090         
22091         time.createChild({
22092             tag: 'tr',
22093             cn: [
22094                 {
22095                     tag: 'td',
22096                     cn: [
22097                         {
22098                             tag: 'a',
22099                             href: '#',
22100                             cls: 'btn',
22101                             cn: [
22102                                 {
22103                                     tag: 'span',
22104                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22105                                 }
22106                             ]
22107                         }
22108                     ]
22109                 },
22110                 {
22111                     tag: 'td',
22112                     cls: 'separator'
22113                 },
22114                 {
22115                     tag: 'td',
22116                     cn: [
22117                         {
22118                             tag: 'a',
22119                             href: '#',
22120                             cls: 'btn',
22121                             cn: [
22122                                 {
22123                                     tag: 'span',
22124                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22125                                 }
22126                             ]
22127                         }
22128                     ]
22129                 },
22130                 {
22131                     tag: 'td',
22132                     cls: 'separator'
22133                 }
22134             ]
22135         });
22136         
22137     },
22138     
22139     update: function()
22140     {
22141         
22142         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22143         
22144         this.fill();
22145     },
22146     
22147     fill: function() 
22148     {
22149         var hours = this.time.getHours();
22150         var minutes = this.time.getMinutes();
22151         var period = 'AM';
22152         
22153         if(hours > 11){
22154             period = 'PM';
22155         }
22156         
22157         if(hours == 0){
22158             hours = 12;
22159         }
22160         
22161         
22162         if(hours > 12){
22163             hours = hours - 12;
22164         }
22165         
22166         if(hours < 10){
22167             hours = '0' + hours;
22168         }
22169         
22170         if(minutes < 10){
22171             minutes = '0' + minutes;
22172         }
22173         
22174         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22175         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22176         this.pop.select('button', true).first().dom.innerHTML = period;
22177         
22178     },
22179     
22180     place: function()
22181     {   
22182         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22183         
22184         var cls = ['bottom'];
22185         
22186         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22187             cls.pop();
22188             cls.push('top');
22189         }
22190         
22191         cls.push('right');
22192         
22193         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22194             cls.pop();
22195             cls.push('left');
22196         }
22197         
22198         this.picker().addClass(cls.join('-'));
22199         
22200         var _this = this;
22201         
22202         Roo.each(cls, function(c){
22203             if(c == 'bottom'){
22204                 _this.picker().setTop(_this.inputEl().getHeight());
22205                 return;
22206             }
22207             if(c == 'top'){
22208                 _this.picker().setTop(0 - _this.picker().getHeight());
22209                 return;
22210             }
22211             
22212             if(c == 'left'){
22213                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22214                 return;
22215             }
22216             if(c == 'right'){
22217                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22218                 return;
22219             }
22220         });
22221         
22222     },
22223   
22224     onFocus : function()
22225     {
22226         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22227         this.show();
22228     },
22229     
22230     onBlur : function()
22231     {
22232         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22233         this.hide();
22234     },
22235     
22236     show : function()
22237     {
22238         this.picker().show();
22239         this.pop.show();
22240         this.update();
22241         this.place();
22242         
22243         this.fireEvent('show', this, this.date);
22244     },
22245     
22246     hide : function()
22247     {
22248         this.picker().hide();
22249         this.pop.hide();
22250         
22251         this.fireEvent('hide', this, this.date);
22252     },
22253     
22254     setTime : function()
22255     {
22256         this.hide();
22257         this.setValue(this.time.format(this.format));
22258         
22259         this.fireEvent('select', this, this.date);
22260         
22261         
22262     },
22263     
22264     onMousedown: function(e){
22265         e.stopPropagation();
22266         e.preventDefault();
22267     },
22268     
22269     onIncrementHours: function()
22270     {
22271         Roo.log('onIncrementHours');
22272         this.time = this.time.add(Date.HOUR, 1);
22273         this.update();
22274         
22275     },
22276     
22277     onDecrementHours: function()
22278     {
22279         Roo.log('onDecrementHours');
22280         this.time = this.time.add(Date.HOUR, -1);
22281         this.update();
22282     },
22283     
22284     onIncrementMinutes: function()
22285     {
22286         Roo.log('onIncrementMinutes');
22287         this.time = this.time.add(Date.MINUTE, 1);
22288         this.update();
22289     },
22290     
22291     onDecrementMinutes: function()
22292     {
22293         Roo.log('onDecrementMinutes');
22294         this.time = this.time.add(Date.MINUTE, -1);
22295         this.update();
22296     },
22297     
22298     onTogglePeriod: function()
22299     {
22300         Roo.log('onTogglePeriod');
22301         this.time = this.time.add(Date.HOUR, 12);
22302         this.update();
22303     }
22304     
22305    
22306 });
22307
22308 Roo.apply(Roo.bootstrap.TimeField,  {
22309     
22310     content : {
22311         tag: 'tbody',
22312         cn: [
22313             {
22314                 tag: 'tr',
22315                 cn: [
22316                 {
22317                     tag: 'td',
22318                     colspan: '7'
22319                 }
22320                 ]
22321             }
22322         ]
22323     },
22324     
22325     footer : {
22326         tag: 'tfoot',
22327         cn: [
22328             {
22329                 tag: 'tr',
22330                 cn: [
22331                 {
22332                     tag: 'th',
22333                     colspan: '7',
22334                     cls: '',
22335                     cn: [
22336                         {
22337                             tag: 'button',
22338                             cls: 'btn btn-info ok',
22339                             html: 'OK'
22340                         }
22341                     ]
22342                 }
22343
22344                 ]
22345             }
22346         ]
22347     }
22348 });
22349
22350 Roo.apply(Roo.bootstrap.TimeField,  {
22351   
22352     template : {
22353         tag: 'div',
22354         cls: 'datepicker dropdown-menu',
22355         cn: [
22356             {
22357                 tag: 'div',
22358                 cls: 'datepicker-time',
22359                 cn: [
22360                 {
22361                     tag: 'table',
22362                     cls: 'table-condensed',
22363                     cn:[
22364                     Roo.bootstrap.TimeField.content,
22365                     Roo.bootstrap.TimeField.footer
22366                     ]
22367                 }
22368                 ]
22369             }
22370         ]
22371     }
22372 });
22373
22374  
22375
22376  /*
22377  * - LGPL
22378  *
22379  * MonthField
22380  * 
22381  */
22382
22383 /**
22384  * @class Roo.bootstrap.MonthField
22385  * @extends Roo.bootstrap.Input
22386  * Bootstrap MonthField class
22387  * 
22388  * @cfg {String} language default en
22389  * 
22390  * @constructor
22391  * Create a new MonthField
22392  * @param {Object} config The config object
22393  */
22394
22395 Roo.bootstrap.MonthField = function(config){
22396     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22397     
22398     this.addEvents({
22399         /**
22400          * @event show
22401          * Fires when this field show.
22402          * @param {Roo.bootstrap.MonthField} this
22403          * @param {Mixed} date The date value
22404          */
22405         show : true,
22406         /**
22407          * @event show
22408          * Fires when this field hide.
22409          * @param {Roo.bootstrap.MonthField} this
22410          * @param {Mixed} date The date value
22411          */
22412         hide : true,
22413         /**
22414          * @event select
22415          * Fires when select a date.
22416          * @param {Roo.bootstrap.MonthField} this
22417          * @param {String} oldvalue The old value
22418          * @param {String} newvalue The new value
22419          */
22420         select : true
22421     });
22422 };
22423
22424 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22425     
22426     onRender: function(ct, position)
22427     {
22428         
22429         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22430         
22431         this.language = this.language || 'en';
22432         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22433         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22434         
22435         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22436         this.isInline = false;
22437         this.isInput = true;
22438         this.component = this.el.select('.add-on', true).first() || false;
22439         this.component = (this.component && this.component.length === 0) ? false : this.component;
22440         this.hasInput = this.component && this.inputEL().length;
22441         
22442         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22443         
22444         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22445         
22446         this.picker().on('mousedown', this.onMousedown, this);
22447         this.picker().on('click', this.onClick, this);
22448         
22449         this.picker().addClass('datepicker-dropdown');
22450         
22451         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22452             v.setStyle('width', '189px');
22453         });
22454         
22455         this.fillMonths();
22456         
22457         this.update();
22458         
22459         if(this.isInline) {
22460             this.show();
22461         }
22462         
22463     },
22464     
22465     setValue: function(v, suppressEvent)
22466     {   
22467         var o = this.getValue();
22468         
22469         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22470         
22471         this.update();
22472
22473         if(suppressEvent !== true){
22474             this.fireEvent('select', this, o, v);
22475         }
22476         
22477     },
22478     
22479     getValue: function()
22480     {
22481         return this.value;
22482     },
22483     
22484     onClick: function(e) 
22485     {
22486         e.stopPropagation();
22487         e.preventDefault();
22488         
22489         var target = e.getTarget();
22490         
22491         if(target.nodeName.toLowerCase() === 'i'){
22492             target = Roo.get(target).dom.parentNode;
22493         }
22494         
22495         var nodeName = target.nodeName;
22496         var className = target.className;
22497         var html = target.innerHTML;
22498         
22499         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22500             return;
22501         }
22502         
22503         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22504         
22505         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22506         
22507         this.hide();
22508                         
22509     },
22510     
22511     picker : function()
22512     {
22513         return this.pickerEl;
22514     },
22515     
22516     fillMonths: function()
22517     {    
22518         var i = 0;
22519         var months = this.picker().select('>.datepicker-months td', true).first();
22520         
22521         months.dom.innerHTML = '';
22522         
22523         while (i < 12) {
22524             var month = {
22525                 tag: 'span',
22526                 cls: 'month',
22527                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22528             };
22529             
22530             months.createChild(month);
22531         }
22532         
22533     },
22534     
22535     update: function()
22536     {
22537         var _this = this;
22538         
22539         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22540             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22541         }
22542         
22543         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22544             e.removeClass('active');
22545             
22546             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22547                 e.addClass('active');
22548             }
22549         })
22550     },
22551     
22552     place: function()
22553     {
22554         if(this.isInline) {
22555             return;
22556         }
22557         
22558         this.picker().removeClass(['bottom', 'top']);
22559         
22560         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22561             /*
22562              * place to the top of element!
22563              *
22564              */
22565             
22566             this.picker().addClass('top');
22567             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22568             
22569             return;
22570         }
22571         
22572         this.picker().addClass('bottom');
22573         
22574         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22575     },
22576     
22577     onFocus : function()
22578     {
22579         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22580         this.show();
22581     },
22582     
22583     onBlur : function()
22584     {
22585         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22586         
22587         var d = this.inputEl().getValue();
22588         
22589         this.setValue(d);
22590                 
22591         this.hide();
22592     },
22593     
22594     show : function()
22595     {
22596         this.picker().show();
22597         this.picker().select('>.datepicker-months', true).first().show();
22598         this.update();
22599         this.place();
22600         
22601         this.fireEvent('show', this, this.date);
22602     },
22603     
22604     hide : function()
22605     {
22606         if(this.isInline) {
22607             return;
22608         }
22609         this.picker().hide();
22610         this.fireEvent('hide', this, this.date);
22611         
22612     },
22613     
22614     onMousedown: function(e)
22615     {
22616         e.stopPropagation();
22617         e.preventDefault();
22618     },
22619     
22620     keyup: function(e)
22621     {
22622         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22623         this.update();
22624     },
22625
22626     fireKey: function(e)
22627     {
22628         if (!this.picker().isVisible()){
22629             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22630                 this.show();
22631             }
22632             return;
22633         }
22634         
22635         var dir;
22636         
22637         switch(e.keyCode){
22638             case 27: // escape
22639                 this.hide();
22640                 e.preventDefault();
22641                 break;
22642             case 37: // left
22643             case 39: // right
22644                 dir = e.keyCode == 37 ? -1 : 1;
22645                 
22646                 this.vIndex = this.vIndex + dir;
22647                 
22648                 if(this.vIndex < 0){
22649                     this.vIndex = 0;
22650                 }
22651                 
22652                 if(this.vIndex > 11){
22653                     this.vIndex = 11;
22654                 }
22655                 
22656                 if(isNaN(this.vIndex)){
22657                     this.vIndex = 0;
22658                 }
22659                 
22660                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22661                 
22662                 break;
22663             case 38: // up
22664             case 40: // down
22665                 
22666                 dir = e.keyCode == 38 ? -1 : 1;
22667                 
22668                 this.vIndex = this.vIndex + dir * 4;
22669                 
22670                 if(this.vIndex < 0){
22671                     this.vIndex = 0;
22672                 }
22673                 
22674                 if(this.vIndex > 11){
22675                     this.vIndex = 11;
22676                 }
22677                 
22678                 if(isNaN(this.vIndex)){
22679                     this.vIndex = 0;
22680                 }
22681                 
22682                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22683                 break;
22684                 
22685             case 13: // enter
22686                 
22687                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22688                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22689                 }
22690                 
22691                 this.hide();
22692                 e.preventDefault();
22693                 break;
22694             case 9: // tab
22695                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22696                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22697                 }
22698                 this.hide();
22699                 break;
22700             case 16: // shift
22701             case 17: // ctrl
22702             case 18: // alt
22703                 break;
22704             default :
22705                 this.hide();
22706                 
22707         }
22708     },
22709     
22710     remove: function() 
22711     {
22712         this.picker().remove();
22713     }
22714    
22715 });
22716
22717 Roo.apply(Roo.bootstrap.MonthField,  {
22718     
22719     content : {
22720         tag: 'tbody',
22721         cn: [
22722         {
22723             tag: 'tr',
22724             cn: [
22725             {
22726                 tag: 'td',
22727                 colspan: '7'
22728             }
22729             ]
22730         }
22731         ]
22732     },
22733     
22734     dates:{
22735         en: {
22736             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22737             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22738         }
22739     }
22740 });
22741
22742 Roo.apply(Roo.bootstrap.MonthField,  {
22743   
22744     template : {
22745         tag: 'div',
22746         cls: 'datepicker dropdown-menu roo-dynamic',
22747         cn: [
22748             {
22749                 tag: 'div',
22750                 cls: 'datepicker-months',
22751                 cn: [
22752                 {
22753                     tag: 'table',
22754                     cls: 'table-condensed',
22755                     cn:[
22756                         Roo.bootstrap.DateField.content
22757                     ]
22758                 }
22759                 ]
22760             }
22761         ]
22762     }
22763 });
22764
22765  
22766
22767  
22768  /*
22769  * - LGPL
22770  *
22771  * CheckBox
22772  * 
22773  */
22774
22775 /**
22776  * @class Roo.bootstrap.CheckBox
22777  * @extends Roo.bootstrap.Input
22778  * Bootstrap CheckBox class
22779  * 
22780  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22781  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22782  * @cfg {String} boxLabel The text that appears beside the checkbox
22783  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22784  * @cfg {Boolean} checked initnal the element
22785  * @cfg {Boolean} inline inline the element (default false)
22786  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22787  * @cfg {String} tooltip label tooltip
22788  * 
22789  * @constructor
22790  * Create a new CheckBox
22791  * @param {Object} config The config object
22792  */
22793
22794 Roo.bootstrap.CheckBox = function(config){
22795     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22796    
22797     this.addEvents({
22798         /**
22799         * @event check
22800         * Fires when the element is checked or unchecked.
22801         * @param {Roo.bootstrap.CheckBox} this This input
22802         * @param {Boolean} checked The new checked value
22803         */
22804        check : true,
22805        /**
22806         * @event click
22807         * Fires when the element is click.
22808         * @param {Roo.bootstrap.CheckBox} this This input
22809         */
22810        click : true
22811     });
22812     
22813 };
22814
22815 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22816   
22817     inputType: 'checkbox',
22818     inputValue: 1,
22819     valueOff: 0,
22820     boxLabel: false,
22821     checked: false,
22822     weight : false,
22823     inline: false,
22824     tooltip : '',
22825     
22826     // checkbox success does not make any sense really.. 
22827     invalidClass : "",
22828     validClass : "",
22829     
22830     
22831     getAutoCreate : function()
22832     {
22833         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22834         
22835         var id = Roo.id();
22836         
22837         var cfg = {};
22838         
22839         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22840         
22841         if(this.inline){
22842             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22843         }
22844         
22845         var input =  {
22846             tag: 'input',
22847             id : id,
22848             type : this.inputType,
22849             value : this.inputValue,
22850             cls : 'roo-' + this.inputType, //'form-box',
22851             placeholder : this.placeholder || ''
22852             
22853         };
22854         
22855         if(this.inputType != 'radio'){
22856             var hidden =  {
22857                 tag: 'input',
22858                 type : 'hidden',
22859                 cls : 'roo-hidden-value',
22860                 value : this.checked ? this.inputValue : this.valueOff
22861             };
22862         }
22863         
22864             
22865         if (this.weight) { // Validity check?
22866             cfg.cls += " " + this.inputType + "-" + this.weight;
22867         }
22868         
22869         if (this.disabled) {
22870             input.disabled=true;
22871         }
22872         
22873         if(this.checked){
22874             input.checked = this.checked;
22875         }
22876         
22877         if (this.name) {
22878             
22879             input.name = this.name;
22880             
22881             if(this.inputType != 'radio'){
22882                 hidden.name = this.name;
22883                 input.name = '_hidden_' + this.name;
22884             }
22885         }
22886         
22887         if (this.size) {
22888             input.cls += ' input-' + this.size;
22889         }
22890         
22891         var settings=this;
22892         
22893         ['xs','sm','md','lg'].map(function(size){
22894             if (settings[size]) {
22895                 cfg.cls += ' col-' + size + '-' + settings[size];
22896             }
22897         });
22898         
22899         var inputblock = input;
22900          
22901         if (this.before || this.after) {
22902             
22903             inputblock = {
22904                 cls : 'input-group',
22905                 cn :  [] 
22906             };
22907             
22908             if (this.before) {
22909                 inputblock.cn.push({
22910                     tag :'span',
22911                     cls : 'input-group-addon',
22912                     html : this.before
22913                 });
22914             }
22915             
22916             inputblock.cn.push(input);
22917             
22918             if(this.inputType != 'radio'){
22919                 inputblock.cn.push(hidden);
22920             }
22921             
22922             if (this.after) {
22923                 inputblock.cn.push({
22924                     tag :'span',
22925                     cls : 'input-group-addon',
22926                     html : this.after
22927                 });
22928             }
22929             
22930         }
22931         var boxLabelCfg = false;
22932         
22933         if(this.boxLabel){
22934            
22935             boxLabelCfg = {
22936                 tag: 'label',
22937                 //'for': id, // box label is handled by onclick - so no for...
22938                 cls: 'box-label',
22939                 html: this.boxLabel
22940             };
22941             if(this.tooltip){
22942                 boxLabelCfg.tooltip = this.tooltip;
22943             }
22944              
22945         }
22946         
22947         
22948         if (align ==='left' && this.fieldLabel.length) {
22949 //                Roo.log("left and has label");
22950             cfg.cn = [
22951                 {
22952                     tag: 'label',
22953                     'for' :  id,
22954                     cls : 'control-label',
22955                     html : this.fieldLabel
22956                 },
22957                 {
22958                     cls : "", 
22959                     cn: [
22960                         inputblock
22961                     ]
22962                 }
22963             ];
22964             
22965             if (boxLabelCfg) {
22966                 cfg.cn[1].cn.push(boxLabelCfg);
22967             }
22968             
22969             if(this.labelWidth > 12){
22970                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22971             }
22972             
22973             if(this.labelWidth < 13 && this.labelmd == 0){
22974                 this.labelmd = this.labelWidth;
22975             }
22976             
22977             if(this.labellg > 0){
22978                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22979                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22980             }
22981             
22982             if(this.labelmd > 0){
22983                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22984                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22985             }
22986             
22987             if(this.labelsm > 0){
22988                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22989                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22990             }
22991             
22992             if(this.labelxs > 0){
22993                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22994                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22995             }
22996             
22997         } else if ( this.fieldLabel.length) {
22998 //                Roo.log(" label");
22999                 cfg.cn = [
23000                    
23001                     {
23002                         tag: this.boxLabel ? 'span' : 'label',
23003                         'for': id,
23004                         cls: 'control-label box-input-label',
23005                         //cls : 'input-group-addon',
23006                         html : this.fieldLabel
23007                     },
23008                     
23009                     inputblock
23010                     
23011                 ];
23012                 if (boxLabelCfg) {
23013                     cfg.cn.push(boxLabelCfg);
23014                 }
23015
23016         } else {
23017             
23018 //                Roo.log(" no label && no align");
23019                 cfg.cn = [  inputblock ] ;
23020                 if (boxLabelCfg) {
23021                     cfg.cn.push(boxLabelCfg);
23022                 }
23023
23024                 
23025         }
23026         
23027        
23028         
23029         if(this.inputType != 'radio'){
23030             cfg.cn.push(hidden);
23031         }
23032         
23033         return cfg;
23034         
23035     },
23036     
23037     /**
23038      * return the real input element.
23039      */
23040     inputEl: function ()
23041     {
23042         return this.el.select('input.roo-' + this.inputType,true).first();
23043     },
23044     hiddenEl: function ()
23045     {
23046         return this.el.select('input.roo-hidden-value',true).first();
23047     },
23048     
23049     labelEl: function()
23050     {
23051         return this.el.select('label.control-label',true).first();
23052     },
23053     /* depricated... */
23054     
23055     label: function()
23056     {
23057         return this.labelEl();
23058     },
23059     
23060     boxLabelEl: function()
23061     {
23062         return this.el.select('label.box-label',true).first();
23063     },
23064     
23065     initEvents : function()
23066     {
23067 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23068         
23069         this.inputEl().on('click', this.onClick,  this);
23070         
23071         if (this.boxLabel) { 
23072             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23073         }
23074         
23075         this.startValue = this.getValue();
23076         
23077         if(this.groupId){
23078             Roo.bootstrap.CheckBox.register(this);
23079         }
23080     },
23081     
23082     onClick : function(e)
23083     {   
23084         if(this.fireEvent('click', this, e) !== false){
23085             this.setChecked(!this.checked);
23086         }
23087         
23088     },
23089     
23090     setChecked : function(state,suppressEvent)
23091     {
23092         this.startValue = this.getValue();
23093
23094         if(this.inputType == 'radio'){
23095             
23096             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23097                 e.dom.checked = false;
23098             });
23099             
23100             this.inputEl().dom.checked = true;
23101             
23102             this.inputEl().dom.value = this.inputValue;
23103             
23104             if(suppressEvent !== true){
23105                 this.fireEvent('check', this, true);
23106             }
23107             
23108             this.validate();
23109             
23110             return;
23111         }
23112         
23113         this.checked = state;
23114         
23115         this.inputEl().dom.checked = state;
23116         
23117         
23118         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23119         
23120         if(suppressEvent !== true){
23121             this.fireEvent('check', this, state);
23122         }
23123         
23124         this.validate();
23125     },
23126     
23127     getValue : function()
23128     {
23129         if(this.inputType == 'radio'){
23130             return this.getGroupValue();
23131         }
23132         
23133         return this.hiddenEl().dom.value;
23134         
23135     },
23136     
23137     getGroupValue : function()
23138     {
23139         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23140             return '';
23141         }
23142         
23143         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23144     },
23145     
23146     setValue : function(v,suppressEvent)
23147     {
23148         if(this.inputType == 'radio'){
23149             this.setGroupValue(v, suppressEvent);
23150             return;
23151         }
23152         
23153         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23154         
23155         this.validate();
23156     },
23157     
23158     setGroupValue : function(v, suppressEvent)
23159     {
23160         this.startValue = this.getValue();
23161         
23162         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23163             e.dom.checked = false;
23164             
23165             if(e.dom.value == v){
23166                 e.dom.checked = true;
23167             }
23168         });
23169         
23170         if(suppressEvent !== true){
23171             this.fireEvent('check', this, true);
23172         }
23173
23174         this.validate();
23175         
23176         return;
23177     },
23178     
23179     validate : function()
23180     {
23181         if(this.getVisibilityEl().hasClass('hidden')){
23182             return true;
23183         }
23184         
23185         if(
23186                 this.disabled || 
23187                 (this.inputType == 'radio' && this.validateRadio()) ||
23188                 (this.inputType == 'checkbox' && this.validateCheckbox())
23189         ){
23190             this.markValid();
23191             return true;
23192         }
23193         
23194         this.markInvalid();
23195         return false;
23196     },
23197     
23198     validateRadio : function()
23199     {
23200         if(this.getVisibilityEl().hasClass('hidden')){
23201             return true;
23202         }
23203         
23204         if(this.allowBlank){
23205             return true;
23206         }
23207         
23208         var valid = false;
23209         
23210         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23211             if(!e.dom.checked){
23212                 return;
23213             }
23214             
23215             valid = true;
23216             
23217             return false;
23218         });
23219         
23220         return valid;
23221     },
23222     
23223     validateCheckbox : function()
23224     {
23225         if(!this.groupId){
23226             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23227             //return (this.getValue() == this.inputValue) ? true : false;
23228         }
23229         
23230         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23231         
23232         if(!group){
23233             return false;
23234         }
23235         
23236         var r = false;
23237         
23238         for(var i in group){
23239             if(group[i].el.isVisible(true)){
23240                 r = false;
23241                 break;
23242             }
23243             
23244             r = true;
23245         }
23246         
23247         for(var i in group){
23248             if(r){
23249                 break;
23250             }
23251             
23252             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23253         }
23254         
23255         return r;
23256     },
23257     
23258     /**
23259      * Mark this field as valid
23260      */
23261     markValid : function()
23262     {
23263         var _this = this;
23264         
23265         this.fireEvent('valid', this);
23266         
23267         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23268         
23269         if(this.groupId){
23270             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23271         }
23272         
23273         if(label){
23274             label.markValid();
23275         }
23276
23277         if(this.inputType == 'radio'){
23278             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23279                 var fg = e.findParent('.form-group', false, true);
23280                 if (Roo.bootstrap.version == 3) {
23281                     fg.removeClass([_this.invalidClass, _this.validClass]);
23282                     fg.addClass(_this.validClass);
23283                 } else {
23284                     fg.removeClass(['is-valid', 'is-invalid']);
23285                     fg.addClass('is-valid');
23286                 }
23287             });
23288             
23289             return;
23290         }
23291
23292         if(!this.groupId){
23293             var fg = this.el.findParent('.form-group', false, true);
23294             if (Roo.bootstrap.version == 3) {
23295                 fg.removeClass([this.invalidClass, this.validClass]);
23296                 fg.addClass(this.validClass);
23297             } else {
23298                 fg.removeClass(['is-valid', 'is-invalid']);
23299                 fg.addClass('is-valid');
23300             }
23301             return;
23302         }
23303         
23304         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23305         
23306         if(!group){
23307             return;
23308         }
23309         
23310         for(var i in group){
23311             var fg = group[i].el.findParent('.form-group', false, true);
23312             if (Roo.bootstrap.version == 3) {
23313                 fg.removeClass([this.invalidClass, this.validClass]);
23314                 fg.addClass(this.validClass);
23315             } else {
23316                 fg.removeClass(['is-valid', 'is-invalid']);
23317                 fg.addClass('is-valid');
23318             }
23319         }
23320     },
23321     
23322      /**
23323      * Mark this field as invalid
23324      * @param {String} msg The validation message
23325      */
23326     markInvalid : function(msg)
23327     {
23328         if(this.allowBlank){
23329             return;
23330         }
23331         
23332         var _this = this;
23333         
23334         this.fireEvent('invalid', this, msg);
23335         
23336         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23337         
23338         if(this.groupId){
23339             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23340         }
23341         
23342         if(label){
23343             label.markInvalid();
23344         }
23345             
23346         if(this.inputType == 'radio'){
23347             
23348             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23349                 var fg = e.findParent('.form-group', false, true);
23350                 if (Roo.bootstrap.version == 3) {
23351                     fg.removeClass([_this.invalidClass, _this.validClass]);
23352                     fg.addClass(_this.invalidClass);
23353                 } else {
23354                     fg.removeClass(['is-invalid', 'is-valid']);
23355                     fg.addClass('is-invalid');
23356                 }
23357             });
23358             
23359             return;
23360         }
23361         
23362         if(!this.groupId){
23363             var fg = this.el.findParent('.form-group', false, true);
23364             if (Roo.bootstrap.version == 3) {
23365                 fg.removeClass([_this.invalidClass, _this.validClass]);
23366                 fg.addClass(_this.invalidClass);
23367             } else {
23368                 fg.removeClass(['is-invalid', 'is-valid']);
23369                 fg.addClass('is-invalid');
23370             }
23371             return;
23372         }
23373         
23374         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23375         
23376         if(!group){
23377             return;
23378         }
23379         
23380         for(var i in group){
23381             var fg = group[i].el.findParent('.form-group', false, true);
23382             if (Roo.bootstrap.version == 3) {
23383                 fg.removeClass([_this.invalidClass, _this.validClass]);
23384                 fg.addClass(_this.invalidClass);
23385             } else {
23386                 fg.removeClass(['is-invalid', 'is-valid']);
23387                 fg.addClass('is-invalid');
23388             }
23389         }
23390         
23391     },
23392     
23393     clearInvalid : function()
23394     {
23395         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23396         
23397         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23398         
23399         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23400         
23401         if (label && label.iconEl) {
23402             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23403             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23404         }
23405     },
23406     
23407     disable : function()
23408     {
23409         if(this.inputType != 'radio'){
23410             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23411             return;
23412         }
23413         
23414         var _this = this;
23415         
23416         if(this.rendered){
23417             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23418                 _this.getActionEl().addClass(this.disabledClass);
23419                 e.dom.disabled = true;
23420             });
23421         }
23422         
23423         this.disabled = true;
23424         this.fireEvent("disable", this);
23425         return this;
23426     },
23427
23428     enable : function()
23429     {
23430         if(this.inputType != 'radio'){
23431             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23432             return;
23433         }
23434         
23435         var _this = this;
23436         
23437         if(this.rendered){
23438             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23439                 _this.getActionEl().removeClass(this.disabledClass);
23440                 e.dom.disabled = false;
23441             });
23442         }
23443         
23444         this.disabled = false;
23445         this.fireEvent("enable", this);
23446         return this;
23447     },
23448     
23449     setBoxLabel : function(v)
23450     {
23451         this.boxLabel = v;
23452         
23453         if(this.rendered){
23454             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23455         }
23456     }
23457
23458 });
23459
23460 Roo.apply(Roo.bootstrap.CheckBox, {
23461     
23462     groups: {},
23463     
23464      /**
23465     * register a CheckBox Group
23466     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23467     */
23468     register : function(checkbox)
23469     {
23470         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23471             this.groups[checkbox.groupId] = {};
23472         }
23473         
23474         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23475             return;
23476         }
23477         
23478         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23479         
23480     },
23481     /**
23482     * fetch a CheckBox Group based on the group ID
23483     * @param {string} the group ID
23484     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23485     */
23486     get: function(groupId) {
23487         if (typeof(this.groups[groupId]) == 'undefined') {
23488             return false;
23489         }
23490         
23491         return this.groups[groupId] ;
23492     }
23493     
23494     
23495 });
23496 /*
23497  * - LGPL
23498  *
23499  * RadioItem
23500  * 
23501  */
23502
23503 /**
23504  * @class Roo.bootstrap.Radio
23505  * @extends Roo.bootstrap.Component
23506  * Bootstrap Radio class
23507  * @cfg {String} boxLabel - the label associated
23508  * @cfg {String} value - the value of radio
23509  * 
23510  * @constructor
23511  * Create a new Radio
23512  * @param {Object} config The config object
23513  */
23514 Roo.bootstrap.Radio = function(config){
23515     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23516     
23517 };
23518
23519 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23520     
23521     boxLabel : '',
23522     
23523     value : '',
23524     
23525     getAutoCreate : function()
23526     {
23527         var cfg = {
23528             tag : 'div',
23529             cls : 'form-group radio',
23530             cn : [
23531                 {
23532                     tag : 'label',
23533                     cls : 'box-label',
23534                     html : this.boxLabel
23535                 }
23536             ]
23537         };
23538         
23539         return cfg;
23540     },
23541     
23542     initEvents : function() 
23543     {
23544         this.parent().register(this);
23545         
23546         this.el.on('click', this.onClick, this);
23547         
23548     },
23549     
23550     onClick : function(e)
23551     {
23552         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23553             this.setChecked(true);
23554         }
23555     },
23556     
23557     setChecked : function(state, suppressEvent)
23558     {
23559         this.parent().setValue(this.value, suppressEvent);
23560         
23561     },
23562     
23563     setBoxLabel : function(v)
23564     {
23565         this.boxLabel = v;
23566         
23567         if(this.rendered){
23568             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23569         }
23570     }
23571     
23572 });
23573  
23574
23575  /*
23576  * - LGPL
23577  *
23578  * Input
23579  * 
23580  */
23581
23582 /**
23583  * @class Roo.bootstrap.SecurePass
23584  * @extends Roo.bootstrap.Input
23585  * Bootstrap SecurePass class
23586  *
23587  * 
23588  * @constructor
23589  * Create a new SecurePass
23590  * @param {Object} config The config object
23591  */
23592  
23593 Roo.bootstrap.SecurePass = function (config) {
23594     // these go here, so the translation tool can replace them..
23595     this.errors = {
23596         PwdEmpty: "Please type a password, and then retype it to confirm.",
23597         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23598         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23599         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23600         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23601         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23602         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23603         TooWeak: "Your password is Too Weak."
23604     },
23605     this.meterLabel = "Password strength:";
23606     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23607     this.meterClass = [
23608         "roo-password-meter-tooweak", 
23609         "roo-password-meter-weak", 
23610         "roo-password-meter-medium", 
23611         "roo-password-meter-strong", 
23612         "roo-password-meter-grey"
23613     ];
23614     
23615     this.errors = {};
23616     
23617     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23618 }
23619
23620 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23621     /**
23622      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23623      * {
23624      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23625      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23626      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23627      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23628      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23629      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23630      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23631      * })
23632      */
23633     // private
23634     
23635     meterWidth: 300,
23636     errorMsg :'',    
23637     errors: false,
23638     imageRoot: '/',
23639     /**
23640      * @cfg {String/Object} Label for the strength meter (defaults to
23641      * 'Password strength:')
23642      */
23643     // private
23644     meterLabel: '',
23645     /**
23646      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23647      * ['Weak', 'Medium', 'Strong'])
23648      */
23649     // private    
23650     pwdStrengths: false,    
23651     // private
23652     strength: 0,
23653     // private
23654     _lastPwd: null,
23655     // private
23656     kCapitalLetter: 0,
23657     kSmallLetter: 1,
23658     kDigit: 2,
23659     kPunctuation: 3,
23660     
23661     insecure: false,
23662     // private
23663     initEvents: function ()
23664     {
23665         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23666
23667         if (this.el.is('input[type=password]') && Roo.isSafari) {
23668             this.el.on('keydown', this.SafariOnKeyDown, this);
23669         }
23670
23671         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23672     },
23673     // private
23674     onRender: function (ct, position)
23675     {
23676         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23677         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23678         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23679
23680         this.trigger.createChild({
23681                    cn: [
23682                     {
23683                     //id: 'PwdMeter',
23684                     tag: 'div',
23685                     cls: 'roo-password-meter-grey col-xs-12',
23686                     style: {
23687                         //width: 0,
23688                         //width: this.meterWidth + 'px'                                                
23689                         }
23690                     },
23691                     {                            
23692                          cls: 'roo-password-meter-text'                          
23693                     }
23694                 ]            
23695         });
23696
23697          
23698         if (this.hideTrigger) {
23699             this.trigger.setDisplayed(false);
23700         }
23701         this.setSize(this.width || '', this.height || '');
23702     },
23703     // private
23704     onDestroy: function ()
23705     {
23706         if (this.trigger) {
23707             this.trigger.removeAllListeners();
23708             this.trigger.remove();
23709         }
23710         if (this.wrap) {
23711             this.wrap.remove();
23712         }
23713         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23714     },
23715     // private
23716     checkStrength: function ()
23717     {
23718         var pwd = this.inputEl().getValue();
23719         if (pwd == this._lastPwd) {
23720             return;
23721         }
23722
23723         var strength;
23724         if (this.ClientSideStrongPassword(pwd)) {
23725             strength = 3;
23726         } else if (this.ClientSideMediumPassword(pwd)) {
23727             strength = 2;
23728         } else if (this.ClientSideWeakPassword(pwd)) {
23729             strength = 1;
23730         } else {
23731             strength = 0;
23732         }
23733         
23734         Roo.log('strength1: ' + strength);
23735         
23736         //var pm = this.trigger.child('div/div/div').dom;
23737         var pm = this.trigger.child('div/div');
23738         pm.removeClass(this.meterClass);
23739         pm.addClass(this.meterClass[strength]);
23740                 
23741         
23742         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23743                 
23744         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23745         
23746         this._lastPwd = pwd;
23747     },
23748     reset: function ()
23749     {
23750         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23751         
23752         this._lastPwd = '';
23753         
23754         var pm = this.trigger.child('div/div');
23755         pm.removeClass(this.meterClass);
23756         pm.addClass('roo-password-meter-grey');        
23757         
23758         
23759         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23760         
23761         pt.innerHTML = '';
23762         this.inputEl().dom.type='password';
23763     },
23764     // private
23765     validateValue: function (value)
23766     {
23767         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23768             return false;
23769         }
23770         if (value.length == 0) {
23771             if (this.allowBlank) {
23772                 this.clearInvalid();
23773                 return true;
23774             }
23775
23776             this.markInvalid(this.errors.PwdEmpty);
23777             this.errorMsg = this.errors.PwdEmpty;
23778             return false;
23779         }
23780         
23781         if(this.insecure){
23782             return true;
23783         }
23784         
23785         if (!value.match(/[\x21-\x7e]+/)) {
23786             this.markInvalid(this.errors.PwdBadChar);
23787             this.errorMsg = this.errors.PwdBadChar;
23788             return false;
23789         }
23790         if (value.length < 6) {
23791             this.markInvalid(this.errors.PwdShort);
23792             this.errorMsg = this.errors.PwdShort;
23793             return false;
23794         }
23795         if (value.length > 16) {
23796             this.markInvalid(this.errors.PwdLong);
23797             this.errorMsg = this.errors.PwdLong;
23798             return false;
23799         }
23800         var strength;
23801         if (this.ClientSideStrongPassword(value)) {
23802             strength = 3;
23803         } else if (this.ClientSideMediumPassword(value)) {
23804             strength = 2;
23805         } else if (this.ClientSideWeakPassword(value)) {
23806             strength = 1;
23807         } else {
23808             strength = 0;
23809         }
23810
23811         
23812         if (strength < 2) {
23813             //this.markInvalid(this.errors.TooWeak);
23814             this.errorMsg = this.errors.TooWeak;
23815             //return false;
23816         }
23817         
23818         
23819         console.log('strength2: ' + strength);
23820         
23821         //var pm = this.trigger.child('div/div/div').dom;
23822         
23823         var pm = this.trigger.child('div/div');
23824         pm.removeClass(this.meterClass);
23825         pm.addClass(this.meterClass[strength]);
23826                 
23827         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23828                 
23829         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23830         
23831         this.errorMsg = ''; 
23832         return true;
23833     },
23834     // private
23835     CharacterSetChecks: function (type)
23836     {
23837         this.type = type;
23838         this.fResult = false;
23839     },
23840     // private
23841     isctype: function (character, type)
23842     {
23843         switch (type) {  
23844             case this.kCapitalLetter:
23845                 if (character >= 'A' && character <= 'Z') {
23846                     return true;
23847                 }
23848                 break;
23849             
23850             case this.kSmallLetter:
23851                 if (character >= 'a' && character <= 'z') {
23852                     return true;
23853                 }
23854                 break;
23855             
23856             case this.kDigit:
23857                 if (character >= '0' && character <= '9') {
23858                     return true;
23859                 }
23860                 break;
23861             
23862             case this.kPunctuation:
23863                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23864                     return true;
23865                 }
23866                 break;
23867             
23868             default:
23869                 return false;
23870         }
23871
23872     },
23873     // private
23874     IsLongEnough: function (pwd, size)
23875     {
23876         return !(pwd == null || isNaN(size) || pwd.length < size);
23877     },
23878     // private
23879     SpansEnoughCharacterSets: function (word, nb)
23880     {
23881         if (!this.IsLongEnough(word, nb))
23882         {
23883             return false;
23884         }
23885
23886         var characterSetChecks = new Array(
23887             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23888             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23889         );
23890         
23891         for (var index = 0; index < word.length; ++index) {
23892             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23893                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23894                     characterSetChecks[nCharSet].fResult = true;
23895                     break;
23896                 }
23897             }
23898         }
23899
23900         var nCharSets = 0;
23901         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23902             if (characterSetChecks[nCharSet].fResult) {
23903                 ++nCharSets;
23904             }
23905         }
23906
23907         if (nCharSets < nb) {
23908             return false;
23909         }
23910         return true;
23911     },
23912     // private
23913     ClientSideStrongPassword: function (pwd)
23914     {
23915         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23916     },
23917     // private
23918     ClientSideMediumPassword: function (pwd)
23919     {
23920         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23921     },
23922     // private
23923     ClientSideWeakPassword: function (pwd)
23924     {
23925         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23926     }
23927           
23928 })//<script type="text/javascript">
23929
23930 /*
23931  * Based  Ext JS Library 1.1.1
23932  * Copyright(c) 2006-2007, Ext JS, LLC.
23933  * LGPL
23934  *
23935  */
23936  
23937 /**
23938  * @class Roo.HtmlEditorCore
23939  * @extends Roo.Component
23940  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23941  *
23942  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23943  */
23944
23945 Roo.HtmlEditorCore = function(config){
23946     
23947     
23948     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23949     
23950     
23951     this.addEvents({
23952         /**
23953          * @event initialize
23954          * Fires when the editor is fully initialized (including the iframe)
23955          * @param {Roo.HtmlEditorCore} this
23956          */
23957         initialize: true,
23958         /**
23959          * @event activate
23960          * Fires when the editor is first receives the focus. Any insertion must wait
23961          * until after this event.
23962          * @param {Roo.HtmlEditorCore} this
23963          */
23964         activate: true,
23965          /**
23966          * @event beforesync
23967          * Fires before the textarea is updated with content from the editor iframe. Return false
23968          * to cancel the sync.
23969          * @param {Roo.HtmlEditorCore} this
23970          * @param {String} html
23971          */
23972         beforesync: true,
23973          /**
23974          * @event beforepush
23975          * Fires before the iframe editor is updated with content from the textarea. Return false
23976          * to cancel the push.
23977          * @param {Roo.HtmlEditorCore} this
23978          * @param {String} html
23979          */
23980         beforepush: true,
23981          /**
23982          * @event sync
23983          * Fires when the textarea is updated with content from the editor iframe.
23984          * @param {Roo.HtmlEditorCore} this
23985          * @param {String} html
23986          */
23987         sync: true,
23988          /**
23989          * @event push
23990          * Fires when the iframe editor is updated with content from the textarea.
23991          * @param {Roo.HtmlEditorCore} this
23992          * @param {String} html
23993          */
23994         push: true,
23995         
23996         /**
23997          * @event editorevent
23998          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23999          * @param {Roo.HtmlEditorCore} this
24000          */
24001         editorevent: true
24002         
24003     });
24004     
24005     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24006     
24007     // defaults : white / black...
24008     this.applyBlacklists();
24009     
24010     
24011     
24012 };
24013
24014
24015 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24016
24017
24018      /**
24019      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24020      */
24021     
24022     owner : false,
24023     
24024      /**
24025      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24026      *                        Roo.resizable.
24027      */
24028     resizable : false,
24029      /**
24030      * @cfg {Number} height (in pixels)
24031      */   
24032     height: 300,
24033    /**
24034      * @cfg {Number} width (in pixels)
24035      */   
24036     width: 500,
24037     
24038     /**
24039      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24040      * 
24041      */
24042     stylesheets: false,
24043     
24044     // id of frame..
24045     frameId: false,
24046     
24047     // private properties
24048     validationEvent : false,
24049     deferHeight: true,
24050     initialized : false,
24051     activated : false,
24052     sourceEditMode : false,
24053     onFocus : Roo.emptyFn,
24054     iframePad:3,
24055     hideMode:'offsets',
24056     
24057     clearUp: true,
24058     
24059     // blacklist + whitelisted elements..
24060     black: false,
24061     white: false,
24062      
24063     bodyCls : '',
24064
24065     /**
24066      * Protected method that will not generally be called directly. It
24067      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24068      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24069      */
24070     getDocMarkup : function(){
24071         // body styles..
24072         var st = '';
24073         
24074         // inherit styels from page...?? 
24075         if (this.stylesheets === false) {
24076             
24077             Roo.get(document.head).select('style').each(function(node) {
24078                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24079             });
24080             
24081             Roo.get(document.head).select('link').each(function(node) { 
24082                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24083             });
24084             
24085         } else if (!this.stylesheets.length) {
24086                 // simple..
24087                 st = '<style type="text/css">' +
24088                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24089                    '</style>';
24090         } else {
24091             for (var i in this.stylesheets) { 
24092                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24093             }
24094             
24095         }
24096         
24097         st +=  '<style type="text/css">' +
24098             'IMG { cursor: pointer } ' +
24099         '</style>';
24100
24101         var cls = 'roo-htmleditor-body';
24102         
24103         if(this.bodyCls.length){
24104             cls += ' ' + this.bodyCls;
24105         }
24106         
24107         return '<html><head>' + st  +
24108             //<style type="text/css">' +
24109             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24110             //'</style>' +
24111             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24112     },
24113
24114     // private
24115     onRender : function(ct, position)
24116     {
24117         var _t = this;
24118         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24119         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24120         
24121         
24122         this.el.dom.style.border = '0 none';
24123         this.el.dom.setAttribute('tabIndex', -1);
24124         this.el.addClass('x-hidden hide');
24125         
24126         
24127         
24128         if(Roo.isIE){ // fix IE 1px bogus margin
24129             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24130         }
24131        
24132         
24133         this.frameId = Roo.id();
24134         
24135          
24136         
24137         var iframe = this.owner.wrap.createChild({
24138             tag: 'iframe',
24139             cls: 'form-control', // bootstrap..
24140             id: this.frameId,
24141             name: this.frameId,
24142             frameBorder : 'no',
24143             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24144         }, this.el
24145         );
24146         
24147         
24148         this.iframe = iframe.dom;
24149
24150          this.assignDocWin();
24151         
24152         this.doc.designMode = 'on';
24153        
24154         this.doc.open();
24155         this.doc.write(this.getDocMarkup());
24156         this.doc.close();
24157
24158         
24159         var task = { // must defer to wait for browser to be ready
24160             run : function(){
24161                 //console.log("run task?" + this.doc.readyState);
24162                 this.assignDocWin();
24163                 if(this.doc.body || this.doc.readyState == 'complete'){
24164                     try {
24165                         this.doc.designMode="on";
24166                     } catch (e) {
24167                         return;
24168                     }
24169                     Roo.TaskMgr.stop(task);
24170                     this.initEditor.defer(10, this);
24171                 }
24172             },
24173             interval : 10,
24174             duration: 10000,
24175             scope: this
24176         };
24177         Roo.TaskMgr.start(task);
24178
24179     },
24180
24181     // private
24182     onResize : function(w, h)
24183     {
24184          Roo.log('resize: ' +w + ',' + h );
24185         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24186         if(!this.iframe){
24187             return;
24188         }
24189         if(typeof w == 'number'){
24190             
24191             this.iframe.style.width = w + 'px';
24192         }
24193         if(typeof h == 'number'){
24194             
24195             this.iframe.style.height = h + 'px';
24196             if(this.doc){
24197                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24198             }
24199         }
24200         
24201     },
24202
24203     /**
24204      * Toggles the editor between standard and source edit mode.
24205      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24206      */
24207     toggleSourceEdit : function(sourceEditMode){
24208         
24209         this.sourceEditMode = sourceEditMode === true;
24210         
24211         if(this.sourceEditMode){
24212  
24213             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24214             
24215         }else{
24216             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24217             //this.iframe.className = '';
24218             this.deferFocus();
24219         }
24220         //this.setSize(this.owner.wrap.getSize());
24221         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24222     },
24223
24224     
24225   
24226
24227     /**
24228      * Protected method that will not generally be called directly. If you need/want
24229      * custom HTML cleanup, this is the method you should override.
24230      * @param {String} html The HTML to be cleaned
24231      * return {String} The cleaned HTML
24232      */
24233     cleanHtml : function(html){
24234         html = String(html);
24235         if(html.length > 5){
24236             if(Roo.isSafari){ // strip safari nonsense
24237                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24238             }
24239         }
24240         if(html == '&nbsp;'){
24241             html = '';
24242         }
24243         return html;
24244     },
24245
24246     /**
24247      * HTML Editor -> Textarea
24248      * Protected method that will not generally be called directly. Syncs the contents
24249      * of the editor iframe with the textarea.
24250      */
24251     syncValue : function(){
24252         if(this.initialized){
24253             var bd = (this.doc.body || this.doc.documentElement);
24254             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24255             var html = bd.innerHTML;
24256             if(Roo.isSafari){
24257                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24258                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24259                 if(m && m[1]){
24260                     html = '<div style="'+m[0]+'">' + html + '</div>';
24261                 }
24262             }
24263             html = this.cleanHtml(html);
24264             // fix up the special chars.. normaly like back quotes in word...
24265             // however we do not want to do this with chinese..
24266             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24267                 
24268                 var cc = match.charCodeAt();
24269
24270                 // Get the character value, handling surrogate pairs
24271                 if (match.length == 2) {
24272                     // It's a surrogate pair, calculate the Unicode code point
24273                     var high = match.charCodeAt(0) - 0xD800;
24274                     var low  = match.charCodeAt(1) - 0xDC00;
24275                     cc = (high * 0x400) + low + 0x10000;
24276                 }  else if (
24277                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24278                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24279                     (cc >= 0xf900 && cc < 0xfb00 )
24280                 ) {
24281                         return match;
24282                 }  
24283          
24284                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24285                 return "&#" + cc + ";";
24286                 
24287                 
24288             });
24289             
24290             
24291              
24292             if(this.owner.fireEvent('beforesync', this, html) !== false){
24293                 this.el.dom.value = html;
24294                 this.owner.fireEvent('sync', this, html);
24295             }
24296         }
24297     },
24298
24299     /**
24300      * Protected method that will not generally be called directly. Pushes the value of the textarea
24301      * into the iframe editor.
24302      */
24303     pushValue : function(){
24304         if(this.initialized){
24305             var v = this.el.dom.value.trim();
24306             
24307 //            if(v.length < 1){
24308 //                v = '&#160;';
24309 //            }
24310             
24311             if(this.owner.fireEvent('beforepush', this, v) !== false){
24312                 var d = (this.doc.body || this.doc.documentElement);
24313                 d.innerHTML = v;
24314                 this.cleanUpPaste();
24315                 this.el.dom.value = d.innerHTML;
24316                 this.owner.fireEvent('push', this, v);
24317             }
24318         }
24319     },
24320
24321     // private
24322     deferFocus : function(){
24323         this.focus.defer(10, this);
24324     },
24325
24326     // doc'ed in Field
24327     focus : function(){
24328         if(this.win && !this.sourceEditMode){
24329             this.win.focus();
24330         }else{
24331             this.el.focus();
24332         }
24333     },
24334     
24335     assignDocWin: function()
24336     {
24337         var iframe = this.iframe;
24338         
24339          if(Roo.isIE){
24340             this.doc = iframe.contentWindow.document;
24341             this.win = iframe.contentWindow;
24342         } else {
24343 //            if (!Roo.get(this.frameId)) {
24344 //                return;
24345 //            }
24346 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24347 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24348             
24349             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24350                 return;
24351             }
24352             
24353             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24354             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24355         }
24356     },
24357     
24358     // private
24359     initEditor : function(){
24360         //console.log("INIT EDITOR");
24361         this.assignDocWin();
24362         
24363         
24364         
24365         this.doc.designMode="on";
24366         this.doc.open();
24367         this.doc.write(this.getDocMarkup());
24368         this.doc.close();
24369         
24370         var dbody = (this.doc.body || this.doc.documentElement);
24371         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24372         // this copies styles from the containing element into thsi one..
24373         // not sure why we need all of this..
24374         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24375         
24376         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24377         //ss['background-attachment'] = 'fixed'; // w3c
24378         dbody.bgProperties = 'fixed'; // ie
24379         //Roo.DomHelper.applyStyles(dbody, ss);
24380         Roo.EventManager.on(this.doc, {
24381             //'mousedown': this.onEditorEvent,
24382             'mouseup': this.onEditorEvent,
24383             'dblclick': this.onEditorEvent,
24384             'click': this.onEditorEvent,
24385             'keyup': this.onEditorEvent,
24386             buffer:100,
24387             scope: this
24388         });
24389         if(Roo.isGecko){
24390             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24391         }
24392         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24393             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24394         }
24395         this.initialized = true;
24396
24397         this.owner.fireEvent('initialize', this);
24398         this.pushValue();
24399     },
24400
24401     // private
24402     onDestroy : function(){
24403         
24404         
24405         
24406         if(this.rendered){
24407             
24408             //for (var i =0; i < this.toolbars.length;i++) {
24409             //    // fixme - ask toolbars for heights?
24410             //    this.toolbars[i].onDestroy();
24411            // }
24412             
24413             //this.wrap.dom.innerHTML = '';
24414             //this.wrap.remove();
24415         }
24416     },
24417
24418     // private
24419     onFirstFocus : function(){
24420         
24421         this.assignDocWin();
24422         
24423         
24424         this.activated = true;
24425          
24426     
24427         if(Roo.isGecko){ // prevent silly gecko errors
24428             this.win.focus();
24429             var s = this.win.getSelection();
24430             if(!s.focusNode || s.focusNode.nodeType != 3){
24431                 var r = s.getRangeAt(0);
24432                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24433                 r.collapse(true);
24434                 this.deferFocus();
24435             }
24436             try{
24437                 this.execCmd('useCSS', true);
24438                 this.execCmd('styleWithCSS', false);
24439             }catch(e){}
24440         }
24441         this.owner.fireEvent('activate', this);
24442     },
24443
24444     // private
24445     adjustFont: function(btn){
24446         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24447         //if(Roo.isSafari){ // safari
24448         //    adjust *= 2;
24449        // }
24450         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24451         if(Roo.isSafari){ // safari
24452             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24453             v =  (v < 10) ? 10 : v;
24454             v =  (v > 48) ? 48 : v;
24455             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24456             
24457         }
24458         
24459         
24460         v = Math.max(1, v+adjust);
24461         
24462         this.execCmd('FontSize', v  );
24463     },
24464
24465     onEditorEvent : function(e)
24466     {
24467         this.owner.fireEvent('editorevent', this, e);
24468       //  this.updateToolbar();
24469         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24470     },
24471
24472     insertTag : function(tg)
24473     {
24474         // could be a bit smarter... -> wrap the current selected tRoo..
24475         if (tg.toLowerCase() == 'span' ||
24476             tg.toLowerCase() == 'code' ||
24477             tg.toLowerCase() == 'sup' ||
24478             tg.toLowerCase() == 'sub' 
24479             ) {
24480             
24481             range = this.createRange(this.getSelection());
24482             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24483             wrappingNode.appendChild(range.extractContents());
24484             range.insertNode(wrappingNode);
24485
24486             return;
24487             
24488             
24489             
24490         }
24491         this.execCmd("formatblock",   tg);
24492         
24493     },
24494     
24495     insertText : function(txt)
24496     {
24497         
24498         
24499         var range = this.createRange();
24500         range.deleteContents();
24501                //alert(Sender.getAttribute('label'));
24502                
24503         range.insertNode(this.doc.createTextNode(txt));
24504     } ,
24505     
24506      
24507
24508     /**
24509      * Executes a Midas editor command on the editor document and performs necessary focus and
24510      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24511      * @param {String} cmd The Midas command
24512      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24513      */
24514     relayCmd : function(cmd, value){
24515         this.win.focus();
24516         this.execCmd(cmd, value);
24517         this.owner.fireEvent('editorevent', this);
24518         //this.updateToolbar();
24519         this.owner.deferFocus();
24520     },
24521
24522     /**
24523      * Executes a Midas editor command directly on the editor document.
24524      * For visual commands, you should use {@link #relayCmd} instead.
24525      * <b>This should only be called after the editor is initialized.</b>
24526      * @param {String} cmd The Midas command
24527      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24528      */
24529     execCmd : function(cmd, value){
24530         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24531         this.syncValue();
24532     },
24533  
24534  
24535    
24536     /**
24537      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24538      * to insert tRoo.
24539      * @param {String} text | dom node.. 
24540      */
24541     insertAtCursor : function(text)
24542     {
24543         
24544         if(!this.activated){
24545             return;
24546         }
24547         /*
24548         if(Roo.isIE){
24549             this.win.focus();
24550             var r = this.doc.selection.createRange();
24551             if(r){
24552                 r.collapse(true);
24553                 r.pasteHTML(text);
24554                 this.syncValue();
24555                 this.deferFocus();
24556             
24557             }
24558             return;
24559         }
24560         */
24561         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24562             this.win.focus();
24563             
24564             
24565             // from jquery ui (MIT licenced)
24566             var range, node;
24567             var win = this.win;
24568             
24569             if (win.getSelection && win.getSelection().getRangeAt) {
24570                 range = win.getSelection().getRangeAt(0);
24571                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24572                 range.insertNode(node);
24573             } else if (win.document.selection && win.document.selection.createRange) {
24574                 // no firefox support
24575                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24576                 win.document.selection.createRange().pasteHTML(txt);
24577             } else {
24578                 // no firefox support
24579                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24580                 this.execCmd('InsertHTML', txt);
24581             } 
24582             
24583             this.syncValue();
24584             
24585             this.deferFocus();
24586         }
24587     },
24588  // private
24589     mozKeyPress : function(e){
24590         if(e.ctrlKey){
24591             var c = e.getCharCode(), cmd;
24592           
24593             if(c > 0){
24594                 c = String.fromCharCode(c).toLowerCase();
24595                 switch(c){
24596                     case 'b':
24597                         cmd = 'bold';
24598                         break;
24599                     case 'i':
24600                         cmd = 'italic';
24601                         break;
24602                     
24603                     case 'u':
24604                         cmd = 'underline';
24605                         break;
24606                     
24607                     case 'v':
24608                         this.cleanUpPaste.defer(100, this);
24609                         return;
24610                         
24611                 }
24612                 if(cmd){
24613                     this.win.focus();
24614                     this.execCmd(cmd);
24615                     this.deferFocus();
24616                     e.preventDefault();
24617                 }
24618                 
24619             }
24620         }
24621     },
24622
24623     // private
24624     fixKeys : function(){ // load time branching for fastest keydown performance
24625         if(Roo.isIE){
24626             return function(e){
24627                 var k = e.getKey(), r;
24628                 if(k == e.TAB){
24629                     e.stopEvent();
24630                     r = this.doc.selection.createRange();
24631                     if(r){
24632                         r.collapse(true);
24633                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24634                         this.deferFocus();
24635                     }
24636                     return;
24637                 }
24638                 
24639                 if(k == e.ENTER){
24640                     r = this.doc.selection.createRange();
24641                     if(r){
24642                         var target = r.parentElement();
24643                         if(!target || target.tagName.toLowerCase() != 'li'){
24644                             e.stopEvent();
24645                             r.pasteHTML('<br />');
24646                             r.collapse(false);
24647                             r.select();
24648                         }
24649                     }
24650                 }
24651                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24652                     this.cleanUpPaste.defer(100, this);
24653                     return;
24654                 }
24655                 
24656                 
24657             };
24658         }else if(Roo.isOpera){
24659             return function(e){
24660                 var k = e.getKey();
24661                 if(k == e.TAB){
24662                     e.stopEvent();
24663                     this.win.focus();
24664                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24665                     this.deferFocus();
24666                 }
24667                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24668                     this.cleanUpPaste.defer(100, this);
24669                     return;
24670                 }
24671                 
24672             };
24673         }else if(Roo.isSafari){
24674             return function(e){
24675                 var k = e.getKey();
24676                 
24677                 if(k == e.TAB){
24678                     e.stopEvent();
24679                     this.execCmd('InsertText','\t');
24680                     this.deferFocus();
24681                     return;
24682                 }
24683                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24684                     this.cleanUpPaste.defer(100, this);
24685                     return;
24686                 }
24687                 
24688              };
24689         }
24690     }(),
24691     
24692     getAllAncestors: function()
24693     {
24694         var p = this.getSelectedNode();
24695         var a = [];
24696         if (!p) {
24697             a.push(p); // push blank onto stack..
24698             p = this.getParentElement();
24699         }
24700         
24701         
24702         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24703             a.push(p);
24704             p = p.parentNode;
24705         }
24706         a.push(this.doc.body);
24707         return a;
24708     },
24709     lastSel : false,
24710     lastSelNode : false,
24711     
24712     
24713     getSelection : function() 
24714     {
24715         this.assignDocWin();
24716         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24717     },
24718     
24719     getSelectedNode: function() 
24720     {
24721         // this may only work on Gecko!!!
24722         
24723         // should we cache this!!!!
24724         
24725         
24726         
24727          
24728         var range = this.createRange(this.getSelection()).cloneRange();
24729         
24730         if (Roo.isIE) {
24731             var parent = range.parentElement();
24732             while (true) {
24733                 var testRange = range.duplicate();
24734                 testRange.moveToElementText(parent);
24735                 if (testRange.inRange(range)) {
24736                     break;
24737                 }
24738                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24739                     break;
24740                 }
24741                 parent = parent.parentElement;
24742             }
24743             return parent;
24744         }
24745         
24746         // is ancestor a text element.
24747         var ac =  range.commonAncestorContainer;
24748         if (ac.nodeType == 3) {
24749             ac = ac.parentNode;
24750         }
24751         
24752         var ar = ac.childNodes;
24753          
24754         var nodes = [];
24755         var other_nodes = [];
24756         var has_other_nodes = false;
24757         for (var i=0;i<ar.length;i++) {
24758             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24759                 continue;
24760             }
24761             // fullly contained node.
24762             
24763             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24764                 nodes.push(ar[i]);
24765                 continue;
24766             }
24767             
24768             // probably selected..
24769             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24770                 other_nodes.push(ar[i]);
24771                 continue;
24772             }
24773             // outer..
24774             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24775                 continue;
24776             }
24777             
24778             
24779             has_other_nodes = true;
24780         }
24781         if (!nodes.length && other_nodes.length) {
24782             nodes= other_nodes;
24783         }
24784         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24785             return false;
24786         }
24787         
24788         return nodes[0];
24789     },
24790     createRange: function(sel)
24791     {
24792         // this has strange effects when using with 
24793         // top toolbar - not sure if it's a great idea.
24794         //this.editor.contentWindow.focus();
24795         if (typeof sel != "undefined") {
24796             try {
24797                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24798             } catch(e) {
24799                 return this.doc.createRange();
24800             }
24801         } else {
24802             return this.doc.createRange();
24803         }
24804     },
24805     getParentElement: function()
24806     {
24807         
24808         this.assignDocWin();
24809         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24810         
24811         var range = this.createRange(sel);
24812          
24813         try {
24814             var p = range.commonAncestorContainer;
24815             while (p.nodeType == 3) { // text node
24816                 p = p.parentNode;
24817             }
24818             return p;
24819         } catch (e) {
24820             return null;
24821         }
24822     
24823     },
24824     /***
24825      *
24826      * Range intersection.. the hard stuff...
24827      *  '-1' = before
24828      *  '0' = hits..
24829      *  '1' = after.
24830      *         [ -- selected range --- ]
24831      *   [fail]                        [fail]
24832      *
24833      *    basically..
24834      *      if end is before start or  hits it. fail.
24835      *      if start is after end or hits it fail.
24836      *
24837      *   if either hits (but other is outside. - then it's not 
24838      *   
24839      *    
24840      **/
24841     
24842     
24843     // @see http://www.thismuchiknow.co.uk/?p=64.
24844     rangeIntersectsNode : function(range, node)
24845     {
24846         var nodeRange = node.ownerDocument.createRange();
24847         try {
24848             nodeRange.selectNode(node);
24849         } catch (e) {
24850             nodeRange.selectNodeContents(node);
24851         }
24852     
24853         var rangeStartRange = range.cloneRange();
24854         rangeStartRange.collapse(true);
24855     
24856         var rangeEndRange = range.cloneRange();
24857         rangeEndRange.collapse(false);
24858     
24859         var nodeStartRange = nodeRange.cloneRange();
24860         nodeStartRange.collapse(true);
24861     
24862         var nodeEndRange = nodeRange.cloneRange();
24863         nodeEndRange.collapse(false);
24864     
24865         return rangeStartRange.compareBoundaryPoints(
24866                  Range.START_TO_START, nodeEndRange) == -1 &&
24867                rangeEndRange.compareBoundaryPoints(
24868                  Range.START_TO_START, nodeStartRange) == 1;
24869         
24870          
24871     },
24872     rangeCompareNode : function(range, node)
24873     {
24874         var nodeRange = node.ownerDocument.createRange();
24875         try {
24876             nodeRange.selectNode(node);
24877         } catch (e) {
24878             nodeRange.selectNodeContents(node);
24879         }
24880         
24881         
24882         range.collapse(true);
24883     
24884         nodeRange.collapse(true);
24885      
24886         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24887         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24888          
24889         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24890         
24891         var nodeIsBefore   =  ss == 1;
24892         var nodeIsAfter    = ee == -1;
24893         
24894         if (nodeIsBefore && nodeIsAfter) {
24895             return 0; // outer
24896         }
24897         if (!nodeIsBefore && nodeIsAfter) {
24898             return 1; //right trailed.
24899         }
24900         
24901         if (nodeIsBefore && !nodeIsAfter) {
24902             return 2;  // left trailed.
24903         }
24904         // fully contined.
24905         return 3;
24906     },
24907
24908     // private? - in a new class?
24909     cleanUpPaste :  function()
24910     {
24911         // cleans up the whole document..
24912         Roo.log('cleanuppaste');
24913         
24914         this.cleanUpChildren(this.doc.body);
24915         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24916         if (clean != this.doc.body.innerHTML) {
24917             this.doc.body.innerHTML = clean;
24918         }
24919         
24920     },
24921     
24922     cleanWordChars : function(input) {// change the chars to hex code
24923         var he = Roo.HtmlEditorCore;
24924         
24925         var output = input;
24926         Roo.each(he.swapCodes, function(sw) { 
24927             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24928             
24929             output = output.replace(swapper, sw[1]);
24930         });
24931         
24932         return output;
24933     },
24934     
24935     
24936     cleanUpChildren : function (n)
24937     {
24938         if (!n.childNodes.length) {
24939             return;
24940         }
24941         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24942            this.cleanUpChild(n.childNodes[i]);
24943         }
24944     },
24945     
24946     
24947         
24948     
24949     cleanUpChild : function (node)
24950     {
24951         var ed = this;
24952         //console.log(node);
24953         if (node.nodeName == "#text") {
24954             // clean up silly Windows -- stuff?
24955             return; 
24956         }
24957         if (node.nodeName == "#comment") {
24958             node.parentNode.removeChild(node);
24959             // clean up silly Windows -- stuff?
24960             return; 
24961         }
24962         var lcname = node.tagName.toLowerCase();
24963         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24964         // whitelist of tags..
24965         
24966         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24967             // remove node.
24968             node.parentNode.removeChild(node);
24969             return;
24970             
24971         }
24972         
24973         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24974         
24975         // spans with no attributes - just remove them..
24976         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24977             remove_keep_children = true;
24978         }
24979         
24980         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24981         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24982         
24983         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24984         //    remove_keep_children = true;
24985         //}
24986         
24987         if (remove_keep_children) {
24988             this.cleanUpChildren(node);
24989             // inserts everything just before this node...
24990             while (node.childNodes.length) {
24991                 var cn = node.childNodes[0];
24992                 node.removeChild(cn);
24993                 node.parentNode.insertBefore(cn, node);
24994             }
24995             node.parentNode.removeChild(node);
24996             return;
24997         }
24998         
24999         if (!node.attributes || !node.attributes.length) {
25000             
25001           
25002             
25003             
25004             this.cleanUpChildren(node);
25005             return;
25006         }
25007         
25008         function cleanAttr(n,v)
25009         {
25010             
25011             if (v.match(/^\./) || v.match(/^\//)) {
25012                 return;
25013             }
25014             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25015                 return;
25016             }
25017             if (v.match(/^#/)) {
25018                 return;
25019             }
25020             if (v.match(/^\{/)) { // allow template editing.
25021                 return;
25022             }
25023 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25024             node.removeAttribute(n);
25025             
25026         }
25027         
25028         var cwhite = this.cwhite;
25029         var cblack = this.cblack;
25030             
25031         function cleanStyle(n,v)
25032         {
25033             if (v.match(/expression/)) { //XSS?? should we even bother..
25034                 node.removeAttribute(n);
25035                 return;
25036             }
25037             
25038             var parts = v.split(/;/);
25039             var clean = [];
25040             
25041             Roo.each(parts, function(p) {
25042                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25043                 if (!p.length) {
25044                     return true;
25045                 }
25046                 var l = p.split(':').shift().replace(/\s+/g,'');
25047                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25048                 
25049                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25050 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25051                     //node.removeAttribute(n);
25052                     return true;
25053                 }
25054                 //Roo.log()
25055                 // only allow 'c whitelisted system attributes'
25056                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25057 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25058                     //node.removeAttribute(n);
25059                     return true;
25060                 }
25061                 
25062                 
25063                  
25064                 
25065                 clean.push(p);
25066                 return true;
25067             });
25068             if (clean.length) { 
25069                 node.setAttribute(n, clean.join(';'));
25070             } else {
25071                 node.removeAttribute(n);
25072             }
25073             
25074         }
25075         
25076         
25077         for (var i = node.attributes.length-1; i > -1 ; i--) {
25078             var a = node.attributes[i];
25079             //console.log(a);
25080             
25081             if (a.name.toLowerCase().substr(0,2)=='on')  {
25082                 node.removeAttribute(a.name);
25083                 continue;
25084             }
25085             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25086                 node.removeAttribute(a.name);
25087                 continue;
25088             }
25089             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25090                 cleanAttr(a.name,a.value); // fixme..
25091                 continue;
25092             }
25093             if (a.name == 'style') {
25094                 cleanStyle(a.name,a.value);
25095                 continue;
25096             }
25097             /// clean up MS crap..
25098             // tecnically this should be a list of valid class'es..
25099             
25100             
25101             if (a.name == 'class') {
25102                 if (a.value.match(/^Mso/)) {
25103                     node.removeAttribute('class');
25104                 }
25105                 
25106                 if (a.value.match(/^body$/)) {
25107                     node.removeAttribute('class');
25108                 }
25109                 continue;
25110             }
25111             
25112             // style cleanup!?
25113             // class cleanup?
25114             
25115         }
25116         
25117         
25118         this.cleanUpChildren(node);
25119         
25120         
25121     },
25122     
25123     /**
25124      * Clean up MS wordisms...
25125      */
25126     cleanWord : function(node)
25127     {
25128         if (!node) {
25129             this.cleanWord(this.doc.body);
25130             return;
25131         }
25132         
25133         if(
25134                 node.nodeName == 'SPAN' &&
25135                 !node.hasAttributes() &&
25136                 node.childNodes.length == 1 &&
25137                 node.firstChild.nodeName == "#text"  
25138         ) {
25139             var textNode = node.firstChild;
25140             node.removeChild(textNode);
25141             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25142                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25143             }
25144             node.parentNode.insertBefore(textNode, node);
25145             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25146                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25147             }
25148             node.parentNode.removeChild(node);
25149         }
25150         
25151         if (node.nodeName == "#text") {
25152             // clean up silly Windows -- stuff?
25153             return; 
25154         }
25155         if (node.nodeName == "#comment") {
25156             node.parentNode.removeChild(node);
25157             // clean up silly Windows -- stuff?
25158             return; 
25159         }
25160         
25161         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25162             node.parentNode.removeChild(node);
25163             return;
25164         }
25165         //Roo.log(node.tagName);
25166         // remove - but keep children..
25167         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25168             //Roo.log('-- removed');
25169             while (node.childNodes.length) {
25170                 var cn = node.childNodes[0];
25171                 node.removeChild(cn);
25172                 node.parentNode.insertBefore(cn, node);
25173                 // move node to parent - and clean it..
25174                 this.cleanWord(cn);
25175             }
25176             node.parentNode.removeChild(node);
25177             /// no need to iterate chidlren = it's got none..
25178             //this.iterateChildren(node, this.cleanWord);
25179             return;
25180         }
25181         // clean styles
25182         if (node.className.length) {
25183             
25184             var cn = node.className.split(/\W+/);
25185             var cna = [];
25186             Roo.each(cn, function(cls) {
25187                 if (cls.match(/Mso[a-zA-Z]+/)) {
25188                     return;
25189                 }
25190                 cna.push(cls);
25191             });
25192             node.className = cna.length ? cna.join(' ') : '';
25193             if (!cna.length) {
25194                 node.removeAttribute("class");
25195             }
25196         }
25197         
25198         if (node.hasAttribute("lang")) {
25199             node.removeAttribute("lang");
25200         }
25201         
25202         if (node.hasAttribute("style")) {
25203             
25204             var styles = node.getAttribute("style").split(";");
25205             var nstyle = [];
25206             Roo.each(styles, function(s) {
25207                 if (!s.match(/:/)) {
25208                     return;
25209                 }
25210                 var kv = s.split(":");
25211                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25212                     return;
25213                 }
25214                 // what ever is left... we allow.
25215                 nstyle.push(s);
25216             });
25217             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25218             if (!nstyle.length) {
25219                 node.removeAttribute('style');
25220             }
25221         }
25222         this.iterateChildren(node, this.cleanWord);
25223         
25224         
25225         
25226     },
25227     /**
25228      * iterateChildren of a Node, calling fn each time, using this as the scole..
25229      * @param {DomNode} node node to iterate children of.
25230      * @param {Function} fn method of this class to call on each item.
25231      */
25232     iterateChildren : function(node, fn)
25233     {
25234         if (!node.childNodes.length) {
25235                 return;
25236         }
25237         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25238            fn.call(this, node.childNodes[i])
25239         }
25240     },
25241     
25242     
25243     /**
25244      * cleanTableWidths.
25245      *
25246      * Quite often pasting from word etc.. results in tables with column and widths.
25247      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25248      *
25249      */
25250     cleanTableWidths : function(node)
25251     {
25252          
25253          
25254         if (!node) {
25255             this.cleanTableWidths(this.doc.body);
25256             return;
25257         }
25258         
25259         // ignore list...
25260         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25261             return; 
25262         }
25263         Roo.log(node.tagName);
25264         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25265             this.iterateChildren(node, this.cleanTableWidths);
25266             return;
25267         }
25268         if (node.hasAttribute('width')) {
25269             node.removeAttribute('width');
25270         }
25271         
25272          
25273         if (node.hasAttribute("style")) {
25274             // pretty basic...
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(/^\s*(width|min-width)\s*$/)) {
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         
25295         this.iterateChildren(node, this.cleanTableWidths);
25296         
25297         
25298     },
25299     
25300     
25301     
25302     
25303     domToHTML : function(currentElement, depth, nopadtext) {
25304         
25305         depth = depth || 0;
25306         nopadtext = nopadtext || false;
25307     
25308         if (!currentElement) {
25309             return this.domToHTML(this.doc.body);
25310         }
25311         
25312         //Roo.log(currentElement);
25313         var j;
25314         var allText = false;
25315         var nodeName = currentElement.nodeName;
25316         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25317         
25318         if  (nodeName == '#text') {
25319             
25320             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25321         }
25322         
25323         
25324         var ret = '';
25325         if (nodeName != 'BODY') {
25326              
25327             var i = 0;
25328             // Prints the node tagName, such as <A>, <IMG>, etc
25329             if (tagName) {
25330                 var attr = [];
25331                 for(i = 0; i < currentElement.attributes.length;i++) {
25332                     // quoting?
25333                     var aname = currentElement.attributes.item(i).name;
25334                     if (!currentElement.attributes.item(i).value.length) {
25335                         continue;
25336                     }
25337                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25338                 }
25339                 
25340                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25341             } 
25342             else {
25343                 
25344                 // eack
25345             }
25346         } else {
25347             tagName = false;
25348         }
25349         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25350             return ret;
25351         }
25352         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25353             nopadtext = true;
25354         }
25355         
25356         
25357         // Traverse the tree
25358         i = 0;
25359         var currentElementChild = currentElement.childNodes.item(i);
25360         var allText = true;
25361         var innerHTML  = '';
25362         lastnode = '';
25363         while (currentElementChild) {
25364             // Formatting code (indent the tree so it looks nice on the screen)
25365             var nopad = nopadtext;
25366             if (lastnode == 'SPAN') {
25367                 nopad  = true;
25368             }
25369             // text
25370             if  (currentElementChild.nodeName == '#text') {
25371                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25372                 toadd = nopadtext ? toadd : toadd.trim();
25373                 if (!nopad && toadd.length > 80) {
25374                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25375                 }
25376                 innerHTML  += toadd;
25377                 
25378                 i++;
25379                 currentElementChild = currentElement.childNodes.item(i);
25380                 lastNode = '';
25381                 continue;
25382             }
25383             allText = false;
25384             
25385             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25386                 
25387             // Recursively traverse the tree structure of the child node
25388             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25389             lastnode = currentElementChild.nodeName;
25390             i++;
25391             currentElementChild=currentElement.childNodes.item(i);
25392         }
25393         
25394         ret += innerHTML;
25395         
25396         if (!allText) {
25397                 // The remaining code is mostly for formatting the tree
25398             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25399         }
25400         
25401         
25402         if (tagName) {
25403             ret+= "</"+tagName+">";
25404         }
25405         return ret;
25406         
25407     },
25408         
25409     applyBlacklists : function()
25410     {
25411         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25412         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25413         
25414         this.white = [];
25415         this.black = [];
25416         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25417             if (b.indexOf(tag) > -1) {
25418                 return;
25419             }
25420             this.white.push(tag);
25421             
25422         }, this);
25423         
25424         Roo.each(w, function(tag) {
25425             if (b.indexOf(tag) > -1) {
25426                 return;
25427             }
25428             if (this.white.indexOf(tag) > -1) {
25429                 return;
25430             }
25431             this.white.push(tag);
25432             
25433         }, this);
25434         
25435         
25436         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25437             if (w.indexOf(tag) > -1) {
25438                 return;
25439             }
25440             this.black.push(tag);
25441             
25442         }, this);
25443         
25444         Roo.each(b, function(tag) {
25445             if (w.indexOf(tag) > -1) {
25446                 return;
25447             }
25448             if (this.black.indexOf(tag) > -1) {
25449                 return;
25450             }
25451             this.black.push(tag);
25452             
25453         }, this);
25454         
25455         
25456         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25457         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25458         
25459         this.cwhite = [];
25460         this.cblack = [];
25461         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25462             if (b.indexOf(tag) > -1) {
25463                 return;
25464             }
25465             this.cwhite.push(tag);
25466             
25467         }, this);
25468         
25469         Roo.each(w, function(tag) {
25470             if (b.indexOf(tag) > -1) {
25471                 return;
25472             }
25473             if (this.cwhite.indexOf(tag) > -1) {
25474                 return;
25475             }
25476             this.cwhite.push(tag);
25477             
25478         }, this);
25479         
25480         
25481         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25482             if (w.indexOf(tag) > -1) {
25483                 return;
25484             }
25485             this.cblack.push(tag);
25486             
25487         }, this);
25488         
25489         Roo.each(b, function(tag) {
25490             if (w.indexOf(tag) > -1) {
25491                 return;
25492             }
25493             if (this.cblack.indexOf(tag) > -1) {
25494                 return;
25495             }
25496             this.cblack.push(tag);
25497             
25498         }, this);
25499     },
25500     
25501     setStylesheets : function(stylesheets)
25502     {
25503         if(typeof(stylesheets) == 'string'){
25504             Roo.get(this.iframe.contentDocument.head).createChild({
25505                 tag : 'link',
25506                 rel : 'stylesheet',
25507                 type : 'text/css',
25508                 href : stylesheets
25509             });
25510             
25511             return;
25512         }
25513         var _this = this;
25514      
25515         Roo.each(stylesheets, function(s) {
25516             if(!s.length){
25517                 return;
25518             }
25519             
25520             Roo.get(_this.iframe.contentDocument.head).createChild({
25521                 tag : 'link',
25522                 rel : 'stylesheet',
25523                 type : 'text/css',
25524                 href : s
25525             });
25526         });
25527
25528         
25529     },
25530     
25531     removeStylesheets : function()
25532     {
25533         var _this = this;
25534         
25535         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25536             s.remove();
25537         });
25538     },
25539     
25540     setStyle : function(style)
25541     {
25542         Roo.get(this.iframe.contentDocument.head).createChild({
25543             tag : 'style',
25544             type : 'text/css',
25545             html : style
25546         });
25547
25548         return;
25549     }
25550     
25551     // hide stuff that is not compatible
25552     /**
25553      * @event blur
25554      * @hide
25555      */
25556     /**
25557      * @event change
25558      * @hide
25559      */
25560     /**
25561      * @event focus
25562      * @hide
25563      */
25564     /**
25565      * @event specialkey
25566      * @hide
25567      */
25568     /**
25569      * @cfg {String} fieldClass @hide
25570      */
25571     /**
25572      * @cfg {String} focusClass @hide
25573      */
25574     /**
25575      * @cfg {String} autoCreate @hide
25576      */
25577     /**
25578      * @cfg {String} inputType @hide
25579      */
25580     /**
25581      * @cfg {String} invalidClass @hide
25582      */
25583     /**
25584      * @cfg {String} invalidText @hide
25585      */
25586     /**
25587      * @cfg {String} msgFx @hide
25588      */
25589     /**
25590      * @cfg {String} validateOnBlur @hide
25591      */
25592 });
25593
25594 Roo.HtmlEditorCore.white = [
25595         'area', 'br', 'img', 'input', 'hr', 'wbr',
25596         
25597        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25598        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25599        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25600        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25601        'table',   'ul',         'xmp', 
25602        
25603        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25604       'thead',   'tr', 
25605      
25606       'dir', 'menu', 'ol', 'ul', 'dl',
25607        
25608       'embed',  'object'
25609 ];
25610
25611
25612 Roo.HtmlEditorCore.black = [
25613     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25614         'applet', // 
25615         'base',   'basefont', 'bgsound', 'blink',  'body', 
25616         'frame',  'frameset', 'head',    'html',   'ilayer', 
25617         'iframe', 'layer',  'link',     'meta',    'object',   
25618         'script', 'style' ,'title',  'xml' // clean later..
25619 ];
25620 Roo.HtmlEditorCore.clean = [
25621     'script', 'style', 'title', 'xml'
25622 ];
25623 Roo.HtmlEditorCore.remove = [
25624     'font'
25625 ];
25626 // attributes..
25627
25628 Roo.HtmlEditorCore.ablack = [
25629     'on'
25630 ];
25631     
25632 Roo.HtmlEditorCore.aclean = [ 
25633     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25634 ];
25635
25636 // protocols..
25637 Roo.HtmlEditorCore.pwhite= [
25638         'http',  'https',  'mailto'
25639 ];
25640
25641 // white listed style attributes.
25642 Roo.HtmlEditorCore.cwhite= [
25643       //  'text-align', /// default is to allow most things..
25644       
25645          
25646 //        'font-size'//??
25647 ];
25648
25649 // black listed style attributes.
25650 Roo.HtmlEditorCore.cblack= [
25651       //  'font-size' -- this can be set by the project 
25652 ];
25653
25654
25655 Roo.HtmlEditorCore.swapCodes   =[ 
25656     [    8211, "--" ], 
25657     [    8212, "--" ], 
25658     [    8216,  "'" ],  
25659     [    8217, "'" ],  
25660     [    8220, '"' ],  
25661     [    8221, '"' ],  
25662     [    8226, "*" ],  
25663     [    8230, "..." ]
25664 ]; 
25665
25666     /*
25667  * - LGPL
25668  *
25669  * HtmlEditor
25670  * 
25671  */
25672
25673 /**
25674  * @class Roo.bootstrap.HtmlEditor
25675  * @extends Roo.bootstrap.TextArea
25676  * Bootstrap HtmlEditor class
25677
25678  * @constructor
25679  * Create a new HtmlEditor
25680  * @param {Object} config The config object
25681  */
25682
25683 Roo.bootstrap.HtmlEditor = function(config){
25684     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25685     if (!this.toolbars) {
25686         this.toolbars = [];
25687     }
25688     
25689     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25690     this.addEvents({
25691             /**
25692              * @event initialize
25693              * Fires when the editor is fully initialized (including the iframe)
25694              * @param {HtmlEditor} this
25695              */
25696             initialize: true,
25697             /**
25698              * @event activate
25699              * Fires when the editor is first receives the focus. Any insertion must wait
25700              * until after this event.
25701              * @param {HtmlEditor} this
25702              */
25703             activate: true,
25704              /**
25705              * @event beforesync
25706              * Fires before the textarea is updated with content from the editor iframe. Return false
25707              * to cancel the sync.
25708              * @param {HtmlEditor} this
25709              * @param {String} html
25710              */
25711             beforesync: true,
25712              /**
25713              * @event beforepush
25714              * Fires before the iframe editor is updated with content from the textarea. Return false
25715              * to cancel the push.
25716              * @param {HtmlEditor} this
25717              * @param {String} html
25718              */
25719             beforepush: true,
25720              /**
25721              * @event sync
25722              * Fires when the textarea is updated with content from the editor iframe.
25723              * @param {HtmlEditor} this
25724              * @param {String} html
25725              */
25726             sync: true,
25727              /**
25728              * @event push
25729              * Fires when the iframe editor is updated with content from the textarea.
25730              * @param {HtmlEditor} this
25731              * @param {String} html
25732              */
25733             push: true,
25734              /**
25735              * @event editmodechange
25736              * Fires when the editor switches edit modes
25737              * @param {HtmlEditor} this
25738              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25739              */
25740             editmodechange: true,
25741             /**
25742              * @event editorevent
25743              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25744              * @param {HtmlEditor} this
25745              */
25746             editorevent: true,
25747             /**
25748              * @event firstfocus
25749              * Fires when on first focus - needed by toolbars..
25750              * @param {HtmlEditor} this
25751              */
25752             firstfocus: true,
25753             /**
25754              * @event autosave
25755              * Auto save the htmlEditor value as a file into Events
25756              * @param {HtmlEditor} this
25757              */
25758             autosave: true,
25759             /**
25760              * @event savedpreview
25761              * preview the saved version of htmlEditor
25762              * @param {HtmlEditor} this
25763              */
25764             savedpreview: true
25765         });
25766 };
25767
25768
25769 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25770     
25771     
25772       /**
25773      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25774      */
25775     toolbars : false,
25776     
25777      /**
25778     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25779     */
25780     btns : [],
25781    
25782      /**
25783      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25784      *                        Roo.resizable.
25785      */
25786     resizable : false,
25787      /**
25788      * @cfg {Number} height (in pixels)
25789      */   
25790     height: 300,
25791    /**
25792      * @cfg {Number} width (in pixels)
25793      */   
25794     width: false,
25795     
25796     /**
25797      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25798      * 
25799      */
25800     stylesheets: false,
25801     
25802     // id of frame..
25803     frameId: false,
25804     
25805     // private properties
25806     validationEvent : false,
25807     deferHeight: true,
25808     initialized : false,
25809     activated : false,
25810     
25811     onFocus : Roo.emptyFn,
25812     iframePad:3,
25813     hideMode:'offsets',
25814     
25815     tbContainer : false,
25816     
25817     bodyCls : '',
25818     
25819     toolbarContainer :function() {
25820         return this.wrap.select('.x-html-editor-tb',true).first();
25821     },
25822
25823     /**
25824      * Protected method that will not generally be called directly. It
25825      * is called when the editor creates its toolbar. Override this method if you need to
25826      * add custom toolbar buttons.
25827      * @param {HtmlEditor} editor
25828      */
25829     createToolbar : function(){
25830         Roo.log('renewing');
25831         Roo.log("create toolbars");
25832         
25833         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25834         this.toolbars[0].render(this.toolbarContainer());
25835         
25836         return;
25837         
25838 //        if (!editor.toolbars || !editor.toolbars.length) {
25839 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25840 //        }
25841 //        
25842 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25843 //            editor.toolbars[i] = Roo.factory(
25844 //                    typeof(editor.toolbars[i]) == 'string' ?
25845 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25846 //                Roo.bootstrap.HtmlEditor);
25847 //            editor.toolbars[i].init(editor);
25848 //        }
25849     },
25850
25851      
25852     // private
25853     onRender : function(ct, position)
25854     {
25855        // Roo.log("Call onRender: " + this.xtype);
25856         var _t = this;
25857         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25858       
25859         this.wrap = this.inputEl().wrap({
25860             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25861         });
25862         
25863         this.editorcore.onRender(ct, position);
25864          
25865         if (this.resizable) {
25866             this.resizeEl = new Roo.Resizable(this.wrap, {
25867                 pinned : true,
25868                 wrap: true,
25869                 dynamic : true,
25870                 minHeight : this.height,
25871                 height: this.height,
25872                 handles : this.resizable,
25873                 width: this.width,
25874                 listeners : {
25875                     resize : function(r, w, h) {
25876                         _t.onResize(w,h); // -something
25877                     }
25878                 }
25879             });
25880             
25881         }
25882         this.createToolbar(this);
25883        
25884         
25885         if(!this.width && this.resizable){
25886             this.setSize(this.wrap.getSize());
25887         }
25888         if (this.resizeEl) {
25889             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25890             // should trigger onReize..
25891         }
25892         
25893     },
25894
25895     // private
25896     onResize : function(w, h)
25897     {
25898         Roo.log('resize: ' +w + ',' + h );
25899         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25900         var ew = false;
25901         var eh = false;
25902         
25903         if(this.inputEl() ){
25904             if(typeof w == 'number'){
25905                 var aw = w - this.wrap.getFrameWidth('lr');
25906                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25907                 ew = aw;
25908             }
25909             if(typeof h == 'number'){
25910                  var tbh = -11;  // fixme it needs to tool bar size!
25911                 for (var i =0; i < this.toolbars.length;i++) {
25912                     // fixme - ask toolbars for heights?
25913                     tbh += this.toolbars[i].el.getHeight();
25914                     //if (this.toolbars[i].footer) {
25915                     //    tbh += this.toolbars[i].footer.el.getHeight();
25916                     //}
25917                 }
25918               
25919                 
25920                 
25921                 
25922                 
25923                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25924                 ah -= 5; // knock a few pixes off for look..
25925                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25926                 var eh = ah;
25927             }
25928         }
25929         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25930         this.editorcore.onResize(ew,eh);
25931         
25932     },
25933
25934     /**
25935      * Toggles the editor between standard and source edit mode.
25936      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25937      */
25938     toggleSourceEdit : function(sourceEditMode)
25939     {
25940         this.editorcore.toggleSourceEdit(sourceEditMode);
25941         
25942         if(this.editorcore.sourceEditMode){
25943             Roo.log('editor - showing textarea');
25944             
25945 //            Roo.log('in');
25946 //            Roo.log(this.syncValue());
25947             this.syncValue();
25948             this.inputEl().removeClass(['hide', 'x-hidden']);
25949             this.inputEl().dom.removeAttribute('tabIndex');
25950             this.inputEl().focus();
25951         }else{
25952             Roo.log('editor - hiding textarea');
25953 //            Roo.log('out')
25954 //            Roo.log(this.pushValue()); 
25955             this.pushValue();
25956             
25957             this.inputEl().addClass(['hide', 'x-hidden']);
25958             this.inputEl().dom.setAttribute('tabIndex', -1);
25959             //this.deferFocus();
25960         }
25961          
25962         if(this.resizable){
25963             this.setSize(this.wrap.getSize());
25964         }
25965         
25966         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25967     },
25968  
25969     // private (for BoxComponent)
25970     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25971
25972     // private (for BoxComponent)
25973     getResizeEl : function(){
25974         return this.wrap;
25975     },
25976
25977     // private (for BoxComponent)
25978     getPositionEl : function(){
25979         return this.wrap;
25980     },
25981
25982     // private
25983     initEvents : function(){
25984         this.originalValue = this.getValue();
25985     },
25986
25987 //    /**
25988 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25989 //     * @method
25990 //     */
25991 //    markInvalid : Roo.emptyFn,
25992 //    /**
25993 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25994 //     * @method
25995 //     */
25996 //    clearInvalid : Roo.emptyFn,
25997
25998     setValue : function(v){
25999         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26000         this.editorcore.pushValue();
26001     },
26002
26003      
26004     // private
26005     deferFocus : function(){
26006         this.focus.defer(10, this);
26007     },
26008
26009     // doc'ed in Field
26010     focus : function(){
26011         this.editorcore.focus();
26012         
26013     },
26014       
26015
26016     // private
26017     onDestroy : function(){
26018         
26019         
26020         
26021         if(this.rendered){
26022             
26023             for (var i =0; i < this.toolbars.length;i++) {
26024                 // fixme - ask toolbars for heights?
26025                 this.toolbars[i].onDestroy();
26026             }
26027             
26028             this.wrap.dom.innerHTML = '';
26029             this.wrap.remove();
26030         }
26031     },
26032
26033     // private
26034     onFirstFocus : function(){
26035         //Roo.log("onFirstFocus");
26036         this.editorcore.onFirstFocus();
26037          for (var i =0; i < this.toolbars.length;i++) {
26038             this.toolbars[i].onFirstFocus();
26039         }
26040         
26041     },
26042     
26043     // private
26044     syncValue : function()
26045     {   
26046         this.editorcore.syncValue();
26047     },
26048     
26049     pushValue : function()
26050     {   
26051         this.editorcore.pushValue();
26052     }
26053      
26054     
26055     // hide stuff that is not compatible
26056     /**
26057      * @event blur
26058      * @hide
26059      */
26060     /**
26061      * @event change
26062      * @hide
26063      */
26064     /**
26065      * @event focus
26066      * @hide
26067      */
26068     /**
26069      * @event specialkey
26070      * @hide
26071      */
26072     /**
26073      * @cfg {String} fieldClass @hide
26074      */
26075     /**
26076      * @cfg {String} focusClass @hide
26077      */
26078     /**
26079      * @cfg {String} autoCreate @hide
26080      */
26081     /**
26082      * @cfg {String} inputType @hide
26083      */
26084      
26085     /**
26086      * @cfg {String} invalidText @hide
26087      */
26088     /**
26089      * @cfg {String} msgFx @hide
26090      */
26091     /**
26092      * @cfg {String} validateOnBlur @hide
26093      */
26094 });
26095  
26096     
26097    
26098    
26099    
26100       
26101 Roo.namespace('Roo.bootstrap.htmleditor');
26102 /**
26103  * @class Roo.bootstrap.HtmlEditorToolbar1
26104  * Basic Toolbar
26105  * 
26106  * @example
26107  * Usage:
26108  *
26109  new Roo.bootstrap.HtmlEditor({
26110     ....
26111     toolbars : [
26112         new Roo.bootstrap.HtmlEditorToolbar1({
26113             disable : { fonts: 1 , format: 1, ..., ... , ...],
26114             btns : [ .... ]
26115         })
26116     }
26117      
26118  * 
26119  * @cfg {Object} disable List of elements to disable..
26120  * @cfg {Array} btns List of additional buttons.
26121  * 
26122  * 
26123  * NEEDS Extra CSS? 
26124  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26125  */
26126  
26127 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26128 {
26129     
26130     Roo.apply(this, config);
26131     
26132     // default disabled, based on 'good practice'..
26133     this.disable = this.disable || {};
26134     Roo.applyIf(this.disable, {
26135         fontSize : true,
26136         colors : true,
26137         specialElements : true
26138     });
26139     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26140     
26141     this.editor = config.editor;
26142     this.editorcore = config.editor.editorcore;
26143     
26144     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26145     
26146     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26147     // dont call parent... till later.
26148 }
26149 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26150      
26151     bar : true,
26152     
26153     editor : false,
26154     editorcore : false,
26155     
26156     
26157     formats : [
26158         "p" ,  
26159         "h1","h2","h3","h4","h5","h6", 
26160         "pre", "code", 
26161         "abbr", "acronym", "address", "cite", "samp", "var",
26162         'div','span'
26163     ],
26164     
26165     onRender : function(ct, position)
26166     {
26167        // Roo.log("Call onRender: " + this.xtype);
26168         
26169        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26170        Roo.log(this.el);
26171        this.el.dom.style.marginBottom = '0';
26172        var _this = this;
26173        var editorcore = this.editorcore;
26174        var editor= this.editor;
26175        
26176        var children = [];
26177        var btn = function(id,cmd , toggle, handler, html){
26178        
26179             var  event = toggle ? 'toggle' : 'click';
26180        
26181             var a = {
26182                 size : 'sm',
26183                 xtype: 'Button',
26184                 xns: Roo.bootstrap,
26185                 //glyphicon : id,
26186                 fa: id,
26187                 cmd : id || cmd,
26188                 enableToggle:toggle !== false,
26189                 html : html || '',
26190                 pressed : toggle ? false : null,
26191                 listeners : {}
26192             };
26193             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26194                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26195             };
26196             children.push(a);
26197             return a;
26198        }
26199        
26200     //    var cb_box = function...
26201         
26202         var style = {
26203                 xtype: 'Button',
26204                 size : 'sm',
26205                 xns: Roo.bootstrap,
26206                 fa : 'font',
26207                 //html : 'submit'
26208                 menu : {
26209                     xtype: 'Menu',
26210                     xns: Roo.bootstrap,
26211                     items:  []
26212                 }
26213         };
26214         Roo.each(this.formats, function(f) {
26215             style.menu.items.push({
26216                 xtype :'MenuItem',
26217                 xns: Roo.bootstrap,
26218                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26219                 tagname : f,
26220                 listeners : {
26221                     click : function()
26222                     {
26223                         editorcore.insertTag(this.tagname);
26224                         editor.focus();
26225                     }
26226                 }
26227                 
26228             });
26229         });
26230         children.push(style);   
26231         
26232         btn('bold',false,true);
26233         btn('italic',false,true);
26234         btn('align-left', 'justifyleft',true);
26235         btn('align-center', 'justifycenter',true);
26236         btn('align-right' , 'justifyright',true);
26237         btn('link', false, false, function(btn) {
26238             //Roo.log("create link?");
26239             var url = prompt(this.createLinkText, this.defaultLinkValue);
26240             if(url && url != 'http:/'+'/'){
26241                 this.editorcore.relayCmd('createlink', url);
26242             }
26243         }),
26244         btn('list','insertunorderedlist',true);
26245         btn('pencil', false,true, function(btn){
26246                 Roo.log(this);
26247                 this.toggleSourceEdit(btn.pressed);
26248         });
26249         
26250         if (this.editor.btns.length > 0) {
26251             for (var i = 0; i<this.editor.btns.length; i++) {
26252                 children.push(this.editor.btns[i]);
26253             }
26254         }
26255         
26256         /*
26257         var cog = {
26258                 xtype: 'Button',
26259                 size : 'sm',
26260                 xns: Roo.bootstrap,
26261                 glyphicon : 'cog',
26262                 //html : 'submit'
26263                 menu : {
26264                     xtype: 'Menu',
26265                     xns: Roo.bootstrap,
26266                     items:  []
26267                 }
26268         };
26269         
26270         cog.menu.items.push({
26271             xtype :'MenuItem',
26272             xns: Roo.bootstrap,
26273             html : Clean styles,
26274             tagname : f,
26275             listeners : {
26276                 click : function()
26277                 {
26278                     editorcore.insertTag(this.tagname);
26279                     editor.focus();
26280                 }
26281             }
26282             
26283         });
26284        */
26285         
26286          
26287        this.xtype = 'NavSimplebar';
26288         
26289         for(var i=0;i< children.length;i++) {
26290             
26291             this.buttons.add(this.addxtypeChild(children[i]));
26292             
26293         }
26294         
26295         editor.on('editorevent', this.updateToolbar, this);
26296     },
26297     onBtnClick : function(id)
26298     {
26299        this.editorcore.relayCmd(id);
26300        this.editorcore.focus();
26301     },
26302     
26303     /**
26304      * Protected method that will not generally be called directly. It triggers
26305      * a toolbar update by reading the markup state of the current selection in the editor.
26306      */
26307     updateToolbar: function(){
26308
26309         if(!this.editorcore.activated){
26310             this.editor.onFirstFocus(); // is this neeed?
26311             return;
26312         }
26313
26314         var btns = this.buttons; 
26315         var doc = this.editorcore.doc;
26316         btns.get('bold').setActive(doc.queryCommandState('bold'));
26317         btns.get('italic').setActive(doc.queryCommandState('italic'));
26318         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26319         
26320         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26321         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26322         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26323         
26324         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26325         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26326          /*
26327         
26328         var ans = this.editorcore.getAllAncestors();
26329         if (this.formatCombo) {
26330             
26331             
26332             var store = this.formatCombo.store;
26333             this.formatCombo.setValue("");
26334             for (var i =0; i < ans.length;i++) {
26335                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26336                     // select it..
26337                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26338                     break;
26339                 }
26340             }
26341         }
26342         
26343         
26344         
26345         // hides menus... - so this cant be on a menu...
26346         Roo.bootstrap.MenuMgr.hideAll();
26347         */
26348         Roo.bootstrap.MenuMgr.hideAll();
26349         //this.editorsyncValue();
26350     },
26351     onFirstFocus: function() {
26352         this.buttons.each(function(item){
26353            item.enable();
26354         });
26355     },
26356     toggleSourceEdit : function(sourceEditMode){
26357         
26358           
26359         if(sourceEditMode){
26360             Roo.log("disabling buttons");
26361            this.buttons.each( function(item){
26362                 if(item.cmd != 'pencil'){
26363                     item.disable();
26364                 }
26365             });
26366           
26367         }else{
26368             Roo.log("enabling buttons");
26369             if(this.editorcore.initialized){
26370                 this.buttons.each( function(item){
26371                     item.enable();
26372                 });
26373             }
26374             
26375         }
26376         Roo.log("calling toggole on editor");
26377         // tell the editor that it's been pressed..
26378         this.editor.toggleSourceEdit(sourceEditMode);
26379        
26380     }
26381 });
26382
26383
26384
26385
26386  
26387 /*
26388  * - LGPL
26389  */
26390
26391 /**
26392  * @class Roo.bootstrap.Markdown
26393  * @extends Roo.bootstrap.TextArea
26394  * Bootstrap Showdown editable area
26395  * @cfg {string} content
26396  * 
26397  * @constructor
26398  * Create a new Showdown
26399  */
26400
26401 Roo.bootstrap.Markdown = function(config){
26402     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26403    
26404 };
26405
26406 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26407     
26408     editing :false,
26409     
26410     initEvents : function()
26411     {
26412         
26413         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26414         this.markdownEl = this.el.createChild({
26415             cls : 'roo-markdown-area'
26416         });
26417         this.inputEl().addClass('d-none');
26418         if (this.getValue() == '') {
26419             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26420             
26421         } else {
26422             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26423         }
26424         this.markdownEl.on('click', this.toggleTextEdit, this);
26425         this.on('blur', this.toggleTextEdit, this);
26426         this.on('specialkey', this.resizeTextArea, this);
26427     },
26428     
26429     toggleTextEdit : function()
26430     {
26431         var sh = this.markdownEl.getHeight();
26432         this.inputEl().addClass('d-none');
26433         this.markdownEl.addClass('d-none');
26434         if (!this.editing) {
26435             // show editor?
26436             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26437             this.inputEl().removeClass('d-none');
26438             this.inputEl().focus();
26439             this.editing = true;
26440             return;
26441         }
26442         // show showdown...
26443         this.updateMarkdown();
26444         this.markdownEl.removeClass('d-none');
26445         this.editing = false;
26446         return;
26447     },
26448     updateMarkdown : function()
26449     {
26450         if (this.getValue() == '') {
26451             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26452             return;
26453         }
26454  
26455         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26456     },
26457     
26458     resizeTextArea: function () {
26459         
26460         var sh = 100;
26461         Roo.log([sh, this.getValue().split("\n").length * 30]);
26462         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26463     },
26464     setValue : function(val)
26465     {
26466         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26467         if (!this.editing) {
26468             this.updateMarkdown();
26469         }
26470         
26471     },
26472     focus : function()
26473     {
26474         if (!this.editing) {
26475             this.toggleTextEdit();
26476         }
26477         
26478     }
26479
26480
26481 });
26482 /**
26483  * @class Roo.bootstrap.Table.AbstractSelectionModel
26484  * @extends Roo.util.Observable
26485  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26486  * implemented by descendant classes.  This class should not be directly instantiated.
26487  * @constructor
26488  */
26489 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26490     this.locked = false;
26491     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26492 };
26493
26494
26495 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26496     /** @ignore Called by the grid automatically. Do not call directly. */
26497     init : function(grid){
26498         this.grid = grid;
26499         this.initEvents();
26500     },
26501
26502     /**
26503      * Locks the selections.
26504      */
26505     lock : function(){
26506         this.locked = true;
26507     },
26508
26509     /**
26510      * Unlocks the selections.
26511      */
26512     unlock : function(){
26513         this.locked = false;
26514     },
26515
26516     /**
26517      * Returns true if the selections are locked.
26518      * @return {Boolean}
26519      */
26520     isLocked : function(){
26521         return this.locked;
26522     },
26523     
26524     
26525     initEvents : function ()
26526     {
26527         
26528     }
26529 });
26530 /**
26531  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26532  * @class Roo.bootstrap.Table.RowSelectionModel
26533  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26534  * It supports multiple selections and keyboard selection/navigation. 
26535  * @constructor
26536  * @param {Object} config
26537  */
26538
26539 Roo.bootstrap.Table.RowSelectionModel = function(config){
26540     Roo.apply(this, config);
26541     this.selections = new Roo.util.MixedCollection(false, function(o){
26542         return o.id;
26543     });
26544
26545     this.last = false;
26546     this.lastActive = false;
26547
26548     this.addEvents({
26549         /**
26550              * @event selectionchange
26551              * Fires when the selection changes
26552              * @param {SelectionModel} this
26553              */
26554             "selectionchange" : true,
26555         /**
26556              * @event afterselectionchange
26557              * Fires after the selection changes (eg. by key press or clicking)
26558              * @param {SelectionModel} this
26559              */
26560             "afterselectionchange" : true,
26561         /**
26562              * @event beforerowselect
26563              * Fires when a row is selected being selected, return false to cancel.
26564              * @param {SelectionModel} this
26565              * @param {Number} rowIndex The selected index
26566              * @param {Boolean} keepExisting False if other selections will be cleared
26567              */
26568             "beforerowselect" : true,
26569         /**
26570              * @event rowselect
26571              * Fires when a row is selected.
26572              * @param {SelectionModel} this
26573              * @param {Number} rowIndex The selected index
26574              * @param {Roo.data.Record} r The record
26575              */
26576             "rowselect" : true,
26577         /**
26578              * @event rowdeselect
26579              * Fires when a row is deselected.
26580              * @param {SelectionModel} this
26581              * @param {Number} rowIndex The selected index
26582              */
26583         "rowdeselect" : true
26584     });
26585     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26586     this.locked = false;
26587  };
26588
26589 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26590     /**
26591      * @cfg {Boolean} singleSelect
26592      * True to allow selection of only one row at a time (defaults to false)
26593      */
26594     singleSelect : false,
26595
26596     // private
26597     initEvents : function()
26598     {
26599
26600         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26601         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26602         //}else{ // allow click to work like normal
26603          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26604         //}
26605         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26606         this.grid.on("rowclick", this.handleMouseDown, this);
26607         
26608         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26609             "up" : function(e){
26610                 if(!e.shiftKey){
26611                     this.selectPrevious(e.shiftKey);
26612                 }else if(this.last !== false && this.lastActive !== false){
26613                     var last = this.last;
26614                     this.selectRange(this.last,  this.lastActive-1);
26615                     this.grid.getView().focusRow(this.lastActive);
26616                     if(last !== false){
26617                         this.last = last;
26618                     }
26619                 }else{
26620                     this.selectFirstRow();
26621                 }
26622                 this.fireEvent("afterselectionchange", this);
26623             },
26624             "down" : function(e){
26625                 if(!e.shiftKey){
26626                     this.selectNext(e.shiftKey);
26627                 }else if(this.last !== false && this.lastActive !== false){
26628                     var last = this.last;
26629                     this.selectRange(this.last,  this.lastActive+1);
26630                     this.grid.getView().focusRow(this.lastActive);
26631                     if(last !== false){
26632                         this.last = last;
26633                     }
26634                 }else{
26635                     this.selectFirstRow();
26636                 }
26637                 this.fireEvent("afterselectionchange", this);
26638             },
26639             scope: this
26640         });
26641         this.grid.store.on('load', function(){
26642             this.selections.clear();
26643         },this);
26644         /*
26645         var view = this.grid.view;
26646         view.on("refresh", this.onRefresh, this);
26647         view.on("rowupdated", this.onRowUpdated, this);
26648         view.on("rowremoved", this.onRemove, this);
26649         */
26650     },
26651
26652     // private
26653     onRefresh : function()
26654     {
26655         var ds = this.grid.store, i, v = this.grid.view;
26656         var s = this.selections;
26657         s.each(function(r){
26658             if((i = ds.indexOfId(r.id)) != -1){
26659                 v.onRowSelect(i);
26660             }else{
26661                 s.remove(r);
26662             }
26663         });
26664     },
26665
26666     // private
26667     onRemove : function(v, index, r){
26668         this.selections.remove(r);
26669     },
26670
26671     // private
26672     onRowUpdated : function(v, index, r){
26673         if(this.isSelected(r)){
26674             v.onRowSelect(index);
26675         }
26676     },
26677
26678     /**
26679      * Select records.
26680      * @param {Array} records The records to select
26681      * @param {Boolean} keepExisting (optional) True to keep existing selections
26682      */
26683     selectRecords : function(records, keepExisting)
26684     {
26685         if(!keepExisting){
26686             this.clearSelections();
26687         }
26688             var ds = this.grid.store;
26689         for(var i = 0, len = records.length; i < len; i++){
26690             this.selectRow(ds.indexOf(records[i]), true);
26691         }
26692     },
26693
26694     /**
26695      * Gets the number of selected rows.
26696      * @return {Number}
26697      */
26698     getCount : function(){
26699         return this.selections.length;
26700     },
26701
26702     /**
26703      * Selects the first row in the grid.
26704      */
26705     selectFirstRow : function(){
26706         this.selectRow(0);
26707     },
26708
26709     /**
26710      * Select the last row.
26711      * @param {Boolean} keepExisting (optional) True to keep existing selections
26712      */
26713     selectLastRow : function(keepExisting){
26714         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26715         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26716     },
26717
26718     /**
26719      * Selects the row immediately following the last selected row.
26720      * @param {Boolean} keepExisting (optional) True to keep existing selections
26721      */
26722     selectNext : function(keepExisting)
26723     {
26724             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26725             this.selectRow(this.last+1, keepExisting);
26726             this.grid.getView().focusRow(this.last);
26727         }
26728     },
26729
26730     /**
26731      * Selects the row that precedes the last selected row.
26732      * @param {Boolean} keepExisting (optional) True to keep existing selections
26733      */
26734     selectPrevious : function(keepExisting){
26735         if(this.last){
26736             this.selectRow(this.last-1, keepExisting);
26737             this.grid.getView().focusRow(this.last);
26738         }
26739     },
26740
26741     /**
26742      * Returns the selected records
26743      * @return {Array} Array of selected records
26744      */
26745     getSelections : function(){
26746         return [].concat(this.selections.items);
26747     },
26748
26749     /**
26750      * Returns the first selected record.
26751      * @return {Record}
26752      */
26753     getSelected : function(){
26754         return this.selections.itemAt(0);
26755     },
26756
26757
26758     /**
26759      * Clears all selections.
26760      */
26761     clearSelections : function(fast)
26762     {
26763         if(this.locked) {
26764             return;
26765         }
26766         if(fast !== true){
26767                 var ds = this.grid.store;
26768             var s = this.selections;
26769             s.each(function(r){
26770                 this.deselectRow(ds.indexOfId(r.id));
26771             }, this);
26772             s.clear();
26773         }else{
26774             this.selections.clear();
26775         }
26776         this.last = false;
26777     },
26778
26779
26780     /**
26781      * Selects all rows.
26782      */
26783     selectAll : function(){
26784         if(this.locked) {
26785             return;
26786         }
26787         this.selections.clear();
26788         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26789             this.selectRow(i, true);
26790         }
26791     },
26792
26793     /**
26794      * Returns True if there is a selection.
26795      * @return {Boolean}
26796      */
26797     hasSelection : function(){
26798         return this.selections.length > 0;
26799     },
26800
26801     /**
26802      * Returns True if the specified row is selected.
26803      * @param {Number/Record} record The record or index of the record to check
26804      * @return {Boolean}
26805      */
26806     isSelected : function(index){
26807             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26808         return (r && this.selections.key(r.id) ? true : false);
26809     },
26810
26811     /**
26812      * Returns True if the specified record id is selected.
26813      * @param {String} id The id of record to check
26814      * @return {Boolean}
26815      */
26816     isIdSelected : function(id){
26817         return (this.selections.key(id) ? true : false);
26818     },
26819
26820
26821     // private
26822     handleMouseDBClick : function(e, t){
26823         
26824     },
26825     // private
26826     handleMouseDown : function(e, t)
26827     {
26828             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26829         if(this.isLocked() || rowIndex < 0 ){
26830             return;
26831         };
26832         if(e.shiftKey && this.last !== false){
26833             var last = this.last;
26834             this.selectRange(last, rowIndex, e.ctrlKey);
26835             this.last = last; // reset the last
26836             t.focus();
26837     
26838         }else{
26839             var isSelected = this.isSelected(rowIndex);
26840             //Roo.log("select row:" + rowIndex);
26841             if(isSelected){
26842                 this.deselectRow(rowIndex);
26843             } else {
26844                         this.selectRow(rowIndex, true);
26845             }
26846     
26847             /*
26848                 if(e.button !== 0 && isSelected){
26849                 alert('rowIndex 2: ' + rowIndex);
26850                     view.focusRow(rowIndex);
26851                 }else if(e.ctrlKey && isSelected){
26852                     this.deselectRow(rowIndex);
26853                 }else if(!isSelected){
26854                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26855                     view.focusRow(rowIndex);
26856                 }
26857             */
26858         }
26859         this.fireEvent("afterselectionchange", this);
26860     },
26861     // private
26862     handleDragableRowClick :  function(grid, rowIndex, e) 
26863     {
26864         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26865             this.selectRow(rowIndex, false);
26866             grid.view.focusRow(rowIndex);
26867              this.fireEvent("afterselectionchange", this);
26868         }
26869     },
26870     
26871     /**
26872      * Selects multiple rows.
26873      * @param {Array} rows Array of the indexes of the row to select
26874      * @param {Boolean} keepExisting (optional) True to keep existing selections
26875      */
26876     selectRows : function(rows, keepExisting){
26877         if(!keepExisting){
26878             this.clearSelections();
26879         }
26880         for(var i = 0, len = rows.length; i < len; i++){
26881             this.selectRow(rows[i], true);
26882         }
26883     },
26884
26885     /**
26886      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26887      * @param {Number} startRow The index of the first row in the range
26888      * @param {Number} endRow The index of the last row in the range
26889      * @param {Boolean} keepExisting (optional) True to retain existing selections
26890      */
26891     selectRange : function(startRow, endRow, keepExisting){
26892         if(this.locked) {
26893             return;
26894         }
26895         if(!keepExisting){
26896             this.clearSelections();
26897         }
26898         if(startRow <= endRow){
26899             for(var i = startRow; i <= endRow; i++){
26900                 this.selectRow(i, true);
26901             }
26902         }else{
26903             for(var i = startRow; i >= endRow; i--){
26904                 this.selectRow(i, true);
26905             }
26906         }
26907     },
26908
26909     /**
26910      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26911      * @param {Number} startRow The index of the first row in the range
26912      * @param {Number} endRow The index of the last row in the range
26913      */
26914     deselectRange : function(startRow, endRow, preventViewNotify){
26915         if(this.locked) {
26916             return;
26917         }
26918         for(var i = startRow; i <= endRow; i++){
26919             this.deselectRow(i, preventViewNotify);
26920         }
26921     },
26922
26923     /**
26924      * Selects a row.
26925      * @param {Number} row The index of the row to select
26926      * @param {Boolean} keepExisting (optional) True to keep existing selections
26927      */
26928     selectRow : function(index, keepExisting, preventViewNotify)
26929     {
26930             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26931             return;
26932         }
26933         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26934             if(!keepExisting || this.singleSelect){
26935                 this.clearSelections();
26936             }
26937             
26938             var r = this.grid.store.getAt(index);
26939             //console.log('selectRow - record id :' + r.id);
26940             
26941             this.selections.add(r);
26942             this.last = this.lastActive = index;
26943             if(!preventViewNotify){
26944                 var proxy = new Roo.Element(
26945                                 this.grid.getRowDom(index)
26946                 );
26947                 proxy.addClass('bg-info info');
26948             }
26949             this.fireEvent("rowselect", this, index, r);
26950             this.fireEvent("selectionchange", this);
26951         }
26952     },
26953
26954     /**
26955      * Deselects a row.
26956      * @param {Number} row The index of the row to deselect
26957      */
26958     deselectRow : function(index, preventViewNotify)
26959     {
26960         if(this.locked) {
26961             return;
26962         }
26963         if(this.last == index){
26964             this.last = false;
26965         }
26966         if(this.lastActive == index){
26967             this.lastActive = false;
26968         }
26969         
26970         var r = this.grid.store.getAt(index);
26971         if (!r) {
26972             return;
26973         }
26974         
26975         this.selections.remove(r);
26976         //.console.log('deselectRow - record id :' + r.id);
26977         if(!preventViewNotify){
26978         
26979             var proxy = new Roo.Element(
26980                 this.grid.getRowDom(index)
26981             );
26982             proxy.removeClass('bg-info info');
26983         }
26984         this.fireEvent("rowdeselect", this, index);
26985         this.fireEvent("selectionchange", this);
26986     },
26987
26988     // private
26989     restoreLast : function(){
26990         if(this._last){
26991             this.last = this._last;
26992         }
26993     },
26994
26995     // private
26996     acceptsNav : function(row, col, cm){
26997         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26998     },
26999
27000     // private
27001     onEditorKey : function(field, e){
27002         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27003         if(k == e.TAB){
27004             e.stopEvent();
27005             ed.completeEdit();
27006             if(e.shiftKey){
27007                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27008             }else{
27009                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27010             }
27011         }else if(k == e.ENTER && !e.ctrlKey){
27012             e.stopEvent();
27013             ed.completeEdit();
27014             if(e.shiftKey){
27015                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27016             }else{
27017                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27018             }
27019         }else if(k == e.ESC){
27020             ed.cancelEdit();
27021         }
27022         if(newCell){
27023             g.startEditing(newCell[0], newCell[1]);
27024         }
27025     }
27026 });
27027 /*
27028  * Based on:
27029  * Ext JS Library 1.1.1
27030  * Copyright(c) 2006-2007, Ext JS, LLC.
27031  *
27032  * Originally Released Under LGPL - original licence link has changed is not relivant.
27033  *
27034  * Fork - LGPL
27035  * <script type="text/javascript">
27036  */
27037  
27038 /**
27039  * @class Roo.bootstrap.PagingToolbar
27040  * @extends Roo.bootstrap.NavSimplebar
27041  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27042  * @constructor
27043  * Create a new PagingToolbar
27044  * @param {Object} config The config object
27045  * @param {Roo.data.Store} store
27046  */
27047 Roo.bootstrap.PagingToolbar = function(config)
27048 {
27049     // old args format still supported... - xtype is prefered..
27050         // created from xtype...
27051     
27052     this.ds = config.dataSource;
27053     
27054     if (config.store && !this.ds) {
27055         this.store= Roo.factory(config.store, Roo.data);
27056         this.ds = this.store;
27057         this.ds.xmodule = this.xmodule || false;
27058     }
27059     
27060     this.toolbarItems = [];
27061     if (config.items) {
27062         this.toolbarItems = config.items;
27063     }
27064     
27065     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27066     
27067     this.cursor = 0;
27068     
27069     if (this.ds) { 
27070         this.bind(this.ds);
27071     }
27072     
27073     if (Roo.bootstrap.version == 4) {
27074         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27075     } else {
27076         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27077     }
27078     
27079 };
27080
27081 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27082     /**
27083      * @cfg {Roo.data.Store} dataSource
27084      * The underlying data store providing the paged data
27085      */
27086     /**
27087      * @cfg {String/HTMLElement/Element} container
27088      * container The id or element that will contain the toolbar
27089      */
27090     /**
27091      * @cfg {Boolean} displayInfo
27092      * True to display the displayMsg (defaults to false)
27093      */
27094     /**
27095      * @cfg {Number} pageSize
27096      * The number of records to display per page (defaults to 20)
27097      */
27098     pageSize: 20,
27099     /**
27100      * @cfg {String} displayMsg
27101      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27102      */
27103     displayMsg : 'Displaying {0} - {1} of {2}',
27104     /**
27105      * @cfg {String} emptyMsg
27106      * The message to display when no records are found (defaults to "No data to display")
27107      */
27108     emptyMsg : 'No data to display',
27109     /**
27110      * Customizable piece of the default paging text (defaults to "Page")
27111      * @type String
27112      */
27113     beforePageText : "Page",
27114     /**
27115      * Customizable piece of the default paging text (defaults to "of %0")
27116      * @type String
27117      */
27118     afterPageText : "of {0}",
27119     /**
27120      * Customizable piece of the default paging text (defaults to "First Page")
27121      * @type String
27122      */
27123     firstText : "First Page",
27124     /**
27125      * Customizable piece of the default paging text (defaults to "Previous Page")
27126      * @type String
27127      */
27128     prevText : "Previous Page",
27129     /**
27130      * Customizable piece of the default paging text (defaults to "Next Page")
27131      * @type String
27132      */
27133     nextText : "Next Page",
27134     /**
27135      * Customizable piece of the default paging text (defaults to "Last Page")
27136      * @type String
27137      */
27138     lastText : "Last Page",
27139     /**
27140      * Customizable piece of the default paging text (defaults to "Refresh")
27141      * @type String
27142      */
27143     refreshText : "Refresh",
27144
27145     buttons : false,
27146     // private
27147     onRender : function(ct, position) 
27148     {
27149         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27150         this.navgroup.parentId = this.id;
27151         this.navgroup.onRender(this.el, null);
27152         // add the buttons to the navgroup
27153         
27154         if(this.displayInfo){
27155             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27156             this.displayEl = this.el.select('.x-paging-info', true).first();
27157 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27158 //            this.displayEl = navel.el.select('span',true).first();
27159         }
27160         
27161         var _this = this;
27162         
27163         if(this.buttons){
27164             Roo.each(_this.buttons, function(e){ // this might need to use render????
27165                Roo.factory(e).render(_this.el);
27166             });
27167         }
27168             
27169         Roo.each(_this.toolbarItems, function(e) {
27170             _this.navgroup.addItem(e);
27171         });
27172         
27173         
27174         this.first = this.navgroup.addItem({
27175             tooltip: this.firstText,
27176             cls: "prev btn-outline-secondary",
27177             html : ' <i class="fa fa-step-backward"></i>',
27178             disabled: true,
27179             preventDefault: true,
27180             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27181         });
27182         
27183         this.prev =  this.navgroup.addItem({
27184             tooltip: this.prevText,
27185             cls: "prev btn-outline-secondary",
27186             html : ' <i class="fa fa-backward"></i>',
27187             disabled: true,
27188             preventDefault: true,
27189             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27190         });
27191     //this.addSeparator();
27192         
27193         
27194         var field = this.navgroup.addItem( {
27195             tagtype : 'span',
27196             cls : 'x-paging-position  btn-outline-secondary',
27197              disabled: true,
27198             html : this.beforePageText  +
27199                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27200                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27201          } ); //?? escaped?
27202         
27203         this.field = field.el.select('input', true).first();
27204         this.field.on("keydown", this.onPagingKeydown, this);
27205         this.field.on("focus", function(){this.dom.select();});
27206     
27207     
27208         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27209         //this.field.setHeight(18);
27210         //this.addSeparator();
27211         this.next = this.navgroup.addItem({
27212             tooltip: this.nextText,
27213             cls: "next btn-outline-secondary",
27214             html : ' <i class="fa fa-forward"></i>',
27215             disabled: true,
27216             preventDefault: true,
27217             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27218         });
27219         this.last = this.navgroup.addItem({
27220             tooltip: this.lastText,
27221             html : ' <i class="fa fa-step-forward"></i>',
27222             cls: "next btn-outline-secondary",
27223             disabled: true,
27224             preventDefault: true,
27225             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27226         });
27227     //this.addSeparator();
27228         this.loading = this.navgroup.addItem({
27229             tooltip: this.refreshText,
27230             cls: "btn-outline-secondary",
27231             html : ' <i class="fa fa-refresh"></i>',
27232             preventDefault: true,
27233             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27234         });
27235         
27236     },
27237
27238     // private
27239     updateInfo : function(){
27240         if(this.displayEl){
27241             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27242             var msg = count == 0 ?
27243                 this.emptyMsg :
27244                 String.format(
27245                     this.displayMsg,
27246                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27247                 );
27248             this.displayEl.update(msg);
27249         }
27250     },
27251
27252     // private
27253     onLoad : function(ds, r, o)
27254     {
27255         this.cursor = o.params.start ? o.params.start : 0;
27256         
27257         var d = this.getPageData(),
27258             ap = d.activePage,
27259             ps = d.pages;
27260         
27261         
27262         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27263         this.field.dom.value = ap;
27264         this.first.setDisabled(ap == 1);
27265         this.prev.setDisabled(ap == 1);
27266         this.next.setDisabled(ap == ps);
27267         this.last.setDisabled(ap == ps);
27268         this.loading.enable();
27269         this.updateInfo();
27270     },
27271
27272     // private
27273     getPageData : function(){
27274         var total = this.ds.getTotalCount();
27275         return {
27276             total : total,
27277             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27278             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27279         };
27280     },
27281
27282     // private
27283     onLoadError : function(){
27284         this.loading.enable();
27285     },
27286
27287     // private
27288     onPagingKeydown : function(e){
27289         var k = e.getKey();
27290         var d = this.getPageData();
27291         if(k == e.RETURN){
27292             var v = this.field.dom.value, pageNum;
27293             if(!v || isNaN(pageNum = parseInt(v, 10))){
27294                 this.field.dom.value = d.activePage;
27295                 return;
27296             }
27297             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27298             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27299             e.stopEvent();
27300         }
27301         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))
27302         {
27303           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27304           this.field.dom.value = pageNum;
27305           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27306           e.stopEvent();
27307         }
27308         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27309         {
27310           var v = this.field.dom.value, pageNum; 
27311           var increment = (e.shiftKey) ? 10 : 1;
27312           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27313                 increment *= -1;
27314           }
27315           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27316             this.field.dom.value = d.activePage;
27317             return;
27318           }
27319           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27320           {
27321             this.field.dom.value = parseInt(v, 10) + increment;
27322             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27323             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27324           }
27325           e.stopEvent();
27326         }
27327     },
27328
27329     // private
27330     beforeLoad : function(){
27331         if(this.loading){
27332             this.loading.disable();
27333         }
27334     },
27335
27336     // private
27337     onClick : function(which){
27338         
27339         var ds = this.ds;
27340         if (!ds) {
27341             return;
27342         }
27343         
27344         switch(which){
27345             case "first":
27346                 ds.load({params:{start: 0, limit: this.pageSize}});
27347             break;
27348             case "prev":
27349                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27350             break;
27351             case "next":
27352                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27353             break;
27354             case "last":
27355                 var total = ds.getTotalCount();
27356                 var extra = total % this.pageSize;
27357                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27358                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27359             break;
27360             case "refresh":
27361                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27362             break;
27363         }
27364     },
27365
27366     /**
27367      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27368      * @param {Roo.data.Store} store The data store to unbind
27369      */
27370     unbind : function(ds){
27371         ds.un("beforeload", this.beforeLoad, this);
27372         ds.un("load", this.onLoad, this);
27373         ds.un("loadexception", this.onLoadError, this);
27374         ds.un("remove", this.updateInfo, this);
27375         ds.un("add", this.updateInfo, this);
27376         this.ds = undefined;
27377     },
27378
27379     /**
27380      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27381      * @param {Roo.data.Store} store The data store to bind
27382      */
27383     bind : function(ds){
27384         ds.on("beforeload", this.beforeLoad, this);
27385         ds.on("load", this.onLoad, this);
27386         ds.on("loadexception", this.onLoadError, this);
27387         ds.on("remove", this.updateInfo, this);
27388         ds.on("add", this.updateInfo, this);
27389         this.ds = ds;
27390     }
27391 });/*
27392  * - LGPL
27393  *
27394  * element
27395  * 
27396  */
27397
27398 /**
27399  * @class Roo.bootstrap.MessageBar
27400  * @extends Roo.bootstrap.Component
27401  * Bootstrap MessageBar class
27402  * @cfg {String} html contents of the MessageBar
27403  * @cfg {String} weight (info | success | warning | danger) default info
27404  * @cfg {String} beforeClass insert the bar before the given class
27405  * @cfg {Boolean} closable (true | false) default false
27406  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27407  * 
27408  * @constructor
27409  * Create a new Element
27410  * @param {Object} config The config object
27411  */
27412
27413 Roo.bootstrap.MessageBar = function(config){
27414     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27415 };
27416
27417 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27418     
27419     html: '',
27420     weight: 'info',
27421     closable: false,
27422     fixed: false,
27423     beforeClass: 'bootstrap-sticky-wrap',
27424     
27425     getAutoCreate : function(){
27426         
27427         var cfg = {
27428             tag: 'div',
27429             cls: 'alert alert-dismissable alert-' + this.weight,
27430             cn: [
27431                 {
27432                     tag: 'span',
27433                     cls: 'message',
27434                     html: this.html || ''
27435                 }
27436             ]
27437         };
27438         
27439         if(this.fixed){
27440             cfg.cls += ' alert-messages-fixed';
27441         }
27442         
27443         if(this.closable){
27444             cfg.cn.push({
27445                 tag: 'button',
27446                 cls: 'close',
27447                 html: 'x'
27448             });
27449         }
27450         
27451         return cfg;
27452     },
27453     
27454     onRender : function(ct, position)
27455     {
27456         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27457         
27458         if(!this.el){
27459             var cfg = Roo.apply({},  this.getAutoCreate());
27460             cfg.id = Roo.id();
27461             
27462             if (this.cls) {
27463                 cfg.cls += ' ' + this.cls;
27464             }
27465             if (this.style) {
27466                 cfg.style = this.style;
27467             }
27468             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27469             
27470             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27471         }
27472         
27473         this.el.select('>button.close').on('click', this.hide, this);
27474         
27475     },
27476     
27477     show : function()
27478     {
27479         if (!this.rendered) {
27480             this.render();
27481         }
27482         
27483         this.el.show();
27484         
27485         this.fireEvent('show', this);
27486         
27487     },
27488     
27489     hide : function()
27490     {
27491         if (!this.rendered) {
27492             this.render();
27493         }
27494         
27495         this.el.hide();
27496         
27497         this.fireEvent('hide', this);
27498     },
27499     
27500     update : function()
27501     {
27502 //        var e = this.el.dom.firstChild;
27503 //        
27504 //        if(this.closable){
27505 //            e = e.nextSibling;
27506 //        }
27507 //        
27508 //        e.data = this.html || '';
27509
27510         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27511     }
27512    
27513 });
27514
27515  
27516
27517      /*
27518  * - LGPL
27519  *
27520  * Graph
27521  * 
27522  */
27523
27524
27525 /**
27526  * @class Roo.bootstrap.Graph
27527  * @extends Roo.bootstrap.Component
27528  * Bootstrap Graph class
27529 > Prameters
27530  -sm {number} sm 4
27531  -md {number} md 5
27532  @cfg {String} graphtype  bar | vbar | pie
27533  @cfg {number} g_x coodinator | centre x (pie)
27534  @cfg {number} g_y coodinator | centre y (pie)
27535  @cfg {number} g_r radius (pie)
27536  @cfg {number} g_height height of the chart (respected by all elements in the set)
27537  @cfg {number} g_width width of the chart (respected by all elements in the set)
27538  @cfg {Object} title The title of the chart
27539     
27540  -{Array}  values
27541  -opts (object) options for the chart 
27542      o {
27543      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27544      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27545      o vgutter (number)
27546      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.
27547      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27548      o to
27549      o stretch (boolean)
27550      o }
27551  -opts (object) options for the pie
27552      o{
27553      o cut
27554      o startAngle (number)
27555      o endAngle (number)
27556      } 
27557  *
27558  * @constructor
27559  * Create a new Input
27560  * @param {Object} config The config object
27561  */
27562
27563 Roo.bootstrap.Graph = function(config){
27564     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27565     
27566     this.addEvents({
27567         // img events
27568         /**
27569          * @event click
27570          * The img click event for the img.
27571          * @param {Roo.EventObject} e
27572          */
27573         "click" : true
27574     });
27575 };
27576
27577 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27578     
27579     sm: 4,
27580     md: 5,
27581     graphtype: 'bar',
27582     g_height: 250,
27583     g_width: 400,
27584     g_x: 50,
27585     g_y: 50,
27586     g_r: 30,
27587     opts:{
27588         //g_colors: this.colors,
27589         g_type: 'soft',
27590         g_gutter: '20%'
27591
27592     },
27593     title : false,
27594
27595     getAutoCreate : function(){
27596         
27597         var cfg = {
27598             tag: 'div',
27599             html : null
27600         };
27601         
27602         
27603         return  cfg;
27604     },
27605
27606     onRender : function(ct,position){
27607         
27608         
27609         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27610         
27611         if (typeof(Raphael) == 'undefined') {
27612             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27613             return;
27614         }
27615         
27616         this.raphael = Raphael(this.el.dom);
27617         
27618                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27619                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27620                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27621                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27622                 /*
27623                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27624                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27625                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27626                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27627                 
27628                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27629                 r.barchart(330, 10, 300, 220, data1);
27630                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27631                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27632                 */
27633                 
27634                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27635                 // r.barchart(30, 30, 560, 250,  xdata, {
27636                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27637                 //     axis : "0 0 1 1",
27638                 //     axisxlabels :  xdata
27639                 //     //yvalues : cols,
27640                    
27641                 // });
27642 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27643 //        
27644 //        this.load(null,xdata,{
27645 //                axis : "0 0 1 1",
27646 //                axisxlabels :  xdata
27647 //                });
27648
27649     },
27650
27651     load : function(graphtype,xdata,opts)
27652     {
27653         this.raphael.clear();
27654         if(!graphtype) {
27655             graphtype = this.graphtype;
27656         }
27657         if(!opts){
27658             opts = this.opts;
27659         }
27660         var r = this.raphael,
27661             fin = function () {
27662                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27663             },
27664             fout = function () {
27665                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27666             },
27667             pfin = function() {
27668                 this.sector.stop();
27669                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27670
27671                 if (this.label) {
27672                     this.label[0].stop();
27673                     this.label[0].attr({ r: 7.5 });
27674                     this.label[1].attr({ "font-weight": 800 });
27675                 }
27676             },
27677             pfout = function() {
27678                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27679
27680                 if (this.label) {
27681                     this.label[0].animate({ r: 5 }, 500, "bounce");
27682                     this.label[1].attr({ "font-weight": 400 });
27683                 }
27684             };
27685
27686         switch(graphtype){
27687             case 'bar':
27688                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27689                 break;
27690             case 'hbar':
27691                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27692                 break;
27693             case 'pie':
27694 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27695 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27696 //            
27697                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27698                 
27699                 break;
27700
27701         }
27702         
27703         if(this.title){
27704             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27705         }
27706         
27707     },
27708     
27709     setTitle: function(o)
27710     {
27711         this.title = o;
27712     },
27713     
27714     initEvents: function() {
27715         
27716         if(!this.href){
27717             this.el.on('click', this.onClick, this);
27718         }
27719     },
27720     
27721     onClick : function(e)
27722     {
27723         Roo.log('img onclick');
27724         this.fireEvent('click', this, e);
27725     }
27726    
27727 });
27728
27729  
27730 /*
27731  * - LGPL
27732  *
27733  * numberBox
27734  * 
27735  */
27736 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27737
27738 /**
27739  * @class Roo.bootstrap.dash.NumberBox
27740  * @extends Roo.bootstrap.Component
27741  * Bootstrap NumberBox class
27742  * @cfg {String} headline Box headline
27743  * @cfg {String} content Box content
27744  * @cfg {String} icon Box icon
27745  * @cfg {String} footer Footer text
27746  * @cfg {String} fhref Footer href
27747  * 
27748  * @constructor
27749  * Create a new NumberBox
27750  * @param {Object} config The config object
27751  */
27752
27753
27754 Roo.bootstrap.dash.NumberBox = function(config){
27755     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27756     
27757 };
27758
27759 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27760     
27761     headline : '',
27762     content : '',
27763     icon : '',
27764     footer : '',
27765     fhref : '',
27766     ficon : '',
27767     
27768     getAutoCreate : function(){
27769         
27770         var cfg = {
27771             tag : 'div',
27772             cls : 'small-box ',
27773             cn : [
27774                 {
27775                     tag : 'div',
27776                     cls : 'inner',
27777                     cn :[
27778                         {
27779                             tag : 'h3',
27780                             cls : 'roo-headline',
27781                             html : this.headline
27782                         },
27783                         {
27784                             tag : 'p',
27785                             cls : 'roo-content',
27786                             html : this.content
27787                         }
27788                     ]
27789                 }
27790             ]
27791         };
27792         
27793         if(this.icon){
27794             cfg.cn.push({
27795                 tag : 'div',
27796                 cls : 'icon',
27797                 cn :[
27798                     {
27799                         tag : 'i',
27800                         cls : 'ion ' + this.icon
27801                     }
27802                 ]
27803             });
27804         }
27805         
27806         if(this.footer){
27807             var footer = {
27808                 tag : 'a',
27809                 cls : 'small-box-footer',
27810                 href : this.fhref || '#',
27811                 html : this.footer
27812             };
27813             
27814             cfg.cn.push(footer);
27815             
27816         }
27817         
27818         return  cfg;
27819     },
27820
27821     onRender : function(ct,position){
27822         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27823
27824
27825        
27826                 
27827     },
27828
27829     setHeadline: function (value)
27830     {
27831         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27832     },
27833     
27834     setFooter: function (value, href)
27835     {
27836         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27837         
27838         if(href){
27839             this.el.select('a.small-box-footer',true).first().attr('href', href);
27840         }
27841         
27842     },
27843
27844     setContent: function (value)
27845     {
27846         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27847     },
27848
27849     initEvents: function() 
27850     {   
27851         
27852     }
27853     
27854 });
27855
27856  
27857 /*
27858  * - LGPL
27859  *
27860  * TabBox
27861  * 
27862  */
27863 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27864
27865 /**
27866  * @class Roo.bootstrap.dash.TabBox
27867  * @extends Roo.bootstrap.Component
27868  * Bootstrap TabBox class
27869  * @cfg {String} title Title of the TabBox
27870  * @cfg {String} icon Icon of the TabBox
27871  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27872  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27873  * 
27874  * @constructor
27875  * Create a new TabBox
27876  * @param {Object} config The config object
27877  */
27878
27879
27880 Roo.bootstrap.dash.TabBox = function(config){
27881     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27882     this.addEvents({
27883         // raw events
27884         /**
27885          * @event addpane
27886          * When a pane is added
27887          * @param {Roo.bootstrap.dash.TabPane} pane
27888          */
27889         "addpane" : true,
27890         /**
27891          * @event activatepane
27892          * When a pane is activated
27893          * @param {Roo.bootstrap.dash.TabPane} pane
27894          */
27895         "activatepane" : true
27896         
27897          
27898     });
27899     
27900     this.panes = [];
27901 };
27902
27903 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27904
27905     title : '',
27906     icon : false,
27907     showtabs : true,
27908     tabScrollable : false,
27909     
27910     getChildContainer : function()
27911     {
27912         return this.el.select('.tab-content', true).first();
27913     },
27914     
27915     getAutoCreate : function(){
27916         
27917         var header = {
27918             tag: 'li',
27919             cls: 'pull-left header',
27920             html: this.title,
27921             cn : []
27922         };
27923         
27924         if(this.icon){
27925             header.cn.push({
27926                 tag: 'i',
27927                 cls: 'fa ' + this.icon
27928             });
27929         }
27930         
27931         var h = {
27932             tag: 'ul',
27933             cls: 'nav nav-tabs pull-right',
27934             cn: [
27935                 header
27936             ]
27937         };
27938         
27939         if(this.tabScrollable){
27940             h = {
27941                 tag: 'div',
27942                 cls: 'tab-header',
27943                 cn: [
27944                     {
27945                         tag: 'ul',
27946                         cls: 'nav nav-tabs pull-right',
27947                         cn: [
27948                             header
27949                         ]
27950                     }
27951                 ]
27952             };
27953         }
27954         
27955         var cfg = {
27956             tag: 'div',
27957             cls: 'nav-tabs-custom',
27958             cn: [
27959                 h,
27960                 {
27961                     tag: 'div',
27962                     cls: 'tab-content no-padding',
27963                     cn: []
27964                 }
27965             ]
27966         };
27967
27968         return  cfg;
27969     },
27970     initEvents : function()
27971     {
27972         //Roo.log('add add pane handler');
27973         this.on('addpane', this.onAddPane, this);
27974     },
27975      /**
27976      * Updates the box title
27977      * @param {String} html to set the title to.
27978      */
27979     setTitle : function(value)
27980     {
27981         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27982     },
27983     onAddPane : function(pane)
27984     {
27985         this.panes.push(pane);
27986         //Roo.log('addpane');
27987         //Roo.log(pane);
27988         // tabs are rendere left to right..
27989         if(!this.showtabs){
27990             return;
27991         }
27992         
27993         var ctr = this.el.select('.nav-tabs', true).first();
27994          
27995          
27996         var existing = ctr.select('.nav-tab',true);
27997         var qty = existing.getCount();;
27998         
27999         
28000         var tab = ctr.createChild({
28001             tag : 'li',
28002             cls : 'nav-tab' + (qty ? '' : ' active'),
28003             cn : [
28004                 {
28005                     tag : 'a',
28006                     href:'#',
28007                     html : pane.title
28008                 }
28009             ]
28010         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28011         pane.tab = tab;
28012         
28013         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28014         if (!qty) {
28015             pane.el.addClass('active');
28016         }
28017         
28018                 
28019     },
28020     onTabClick : function(ev,un,ob,pane)
28021     {
28022         //Roo.log('tab - prev default');
28023         ev.preventDefault();
28024         
28025         
28026         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28027         pane.tab.addClass('active');
28028         //Roo.log(pane.title);
28029         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28030         // technically we should have a deactivate event.. but maybe add later.
28031         // and it should not de-activate the selected tab...
28032         this.fireEvent('activatepane', pane);
28033         pane.el.addClass('active');
28034         pane.fireEvent('activate');
28035         
28036         
28037     },
28038     
28039     getActivePane : function()
28040     {
28041         var r = false;
28042         Roo.each(this.panes, function(p) {
28043             if(p.el.hasClass('active')){
28044                 r = p;
28045                 return false;
28046             }
28047             
28048             return;
28049         });
28050         
28051         return r;
28052     }
28053     
28054     
28055 });
28056
28057  
28058 /*
28059  * - LGPL
28060  *
28061  * Tab pane
28062  * 
28063  */
28064 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28065 /**
28066  * @class Roo.bootstrap.TabPane
28067  * @extends Roo.bootstrap.Component
28068  * Bootstrap TabPane class
28069  * @cfg {Boolean} active (false | true) Default false
28070  * @cfg {String} title title of panel
28071
28072  * 
28073  * @constructor
28074  * Create a new TabPane
28075  * @param {Object} config The config object
28076  */
28077
28078 Roo.bootstrap.dash.TabPane = function(config){
28079     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28080     
28081     this.addEvents({
28082         // raw events
28083         /**
28084          * @event activate
28085          * When a pane is activated
28086          * @param {Roo.bootstrap.dash.TabPane} pane
28087          */
28088         "activate" : true
28089          
28090     });
28091 };
28092
28093 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28094     
28095     active : false,
28096     title : '',
28097     
28098     // the tabBox that this is attached to.
28099     tab : false,
28100      
28101     getAutoCreate : function() 
28102     {
28103         var cfg = {
28104             tag: 'div',
28105             cls: 'tab-pane'
28106         };
28107         
28108         if(this.active){
28109             cfg.cls += ' active';
28110         }
28111         
28112         return cfg;
28113     },
28114     initEvents  : function()
28115     {
28116         //Roo.log('trigger add pane handler');
28117         this.parent().fireEvent('addpane', this)
28118     },
28119     
28120      /**
28121      * Updates the tab title 
28122      * @param {String} html to set the title to.
28123      */
28124     setTitle: function(str)
28125     {
28126         if (!this.tab) {
28127             return;
28128         }
28129         this.title = str;
28130         this.tab.select('a', true).first().dom.innerHTML = str;
28131         
28132     }
28133     
28134     
28135     
28136 });
28137
28138  
28139
28140
28141  /*
28142  * - LGPL
28143  *
28144  * menu
28145  * 
28146  */
28147 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28148
28149 /**
28150  * @class Roo.bootstrap.menu.Menu
28151  * @extends Roo.bootstrap.Component
28152  * Bootstrap Menu class - container for Menu
28153  * @cfg {String} html Text of the menu
28154  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28155  * @cfg {String} icon Font awesome icon
28156  * @cfg {String} pos Menu align to (top | bottom) default bottom
28157  * 
28158  * 
28159  * @constructor
28160  * Create a new Menu
28161  * @param {Object} config The config object
28162  */
28163
28164
28165 Roo.bootstrap.menu.Menu = function(config){
28166     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28167     
28168     this.addEvents({
28169         /**
28170          * @event beforeshow
28171          * Fires before this menu is displayed
28172          * @param {Roo.bootstrap.menu.Menu} this
28173          */
28174         beforeshow : true,
28175         /**
28176          * @event beforehide
28177          * Fires before this menu is hidden
28178          * @param {Roo.bootstrap.menu.Menu} this
28179          */
28180         beforehide : true,
28181         /**
28182          * @event show
28183          * Fires after this menu is displayed
28184          * @param {Roo.bootstrap.menu.Menu} this
28185          */
28186         show : true,
28187         /**
28188          * @event hide
28189          * Fires after this menu is hidden
28190          * @param {Roo.bootstrap.menu.Menu} this
28191          */
28192         hide : true,
28193         /**
28194          * @event click
28195          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28196          * @param {Roo.bootstrap.menu.Menu} this
28197          * @param {Roo.EventObject} e
28198          */
28199         click : true
28200     });
28201     
28202 };
28203
28204 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28205     
28206     submenu : false,
28207     html : '',
28208     weight : 'default',
28209     icon : false,
28210     pos : 'bottom',
28211     
28212     
28213     getChildContainer : function() {
28214         if(this.isSubMenu){
28215             return this.el;
28216         }
28217         
28218         return this.el.select('ul.dropdown-menu', true).first();  
28219     },
28220     
28221     getAutoCreate : function()
28222     {
28223         var text = [
28224             {
28225                 tag : 'span',
28226                 cls : 'roo-menu-text',
28227                 html : this.html
28228             }
28229         ];
28230         
28231         if(this.icon){
28232             text.unshift({
28233                 tag : 'i',
28234                 cls : 'fa ' + this.icon
28235             })
28236         }
28237         
28238         
28239         var cfg = {
28240             tag : 'div',
28241             cls : 'btn-group',
28242             cn : [
28243                 {
28244                     tag : 'button',
28245                     cls : 'dropdown-button btn btn-' + this.weight,
28246                     cn : text
28247                 },
28248                 {
28249                     tag : 'button',
28250                     cls : 'dropdown-toggle btn btn-' + this.weight,
28251                     cn : [
28252                         {
28253                             tag : 'span',
28254                             cls : 'caret'
28255                         }
28256                     ]
28257                 },
28258                 {
28259                     tag : 'ul',
28260                     cls : 'dropdown-menu'
28261                 }
28262             ]
28263             
28264         };
28265         
28266         if(this.pos == 'top'){
28267             cfg.cls += ' dropup';
28268         }
28269         
28270         if(this.isSubMenu){
28271             cfg = {
28272                 tag : 'ul',
28273                 cls : 'dropdown-menu'
28274             }
28275         }
28276         
28277         return cfg;
28278     },
28279     
28280     onRender : function(ct, position)
28281     {
28282         this.isSubMenu = ct.hasClass('dropdown-submenu');
28283         
28284         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28285     },
28286     
28287     initEvents : function() 
28288     {
28289         if(this.isSubMenu){
28290             return;
28291         }
28292         
28293         this.hidden = true;
28294         
28295         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28296         this.triggerEl.on('click', this.onTriggerPress, this);
28297         
28298         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28299         this.buttonEl.on('click', this.onClick, this);
28300         
28301     },
28302     
28303     list : function()
28304     {
28305         if(this.isSubMenu){
28306             return this.el;
28307         }
28308         
28309         return this.el.select('ul.dropdown-menu', true).first();
28310     },
28311     
28312     onClick : function(e)
28313     {
28314         this.fireEvent("click", this, e);
28315     },
28316     
28317     onTriggerPress  : function(e)
28318     {   
28319         if (this.isVisible()) {
28320             this.hide();
28321         } else {
28322             this.show();
28323         }
28324     },
28325     
28326     isVisible : function(){
28327         return !this.hidden;
28328     },
28329     
28330     show : function()
28331     {
28332         this.fireEvent("beforeshow", this);
28333         
28334         this.hidden = false;
28335         this.el.addClass('open');
28336         
28337         Roo.get(document).on("mouseup", this.onMouseUp, this);
28338         
28339         this.fireEvent("show", this);
28340         
28341         
28342     },
28343     
28344     hide : function()
28345     {
28346         this.fireEvent("beforehide", this);
28347         
28348         this.hidden = true;
28349         this.el.removeClass('open');
28350         
28351         Roo.get(document).un("mouseup", this.onMouseUp);
28352         
28353         this.fireEvent("hide", this);
28354     },
28355     
28356     onMouseUp : function()
28357     {
28358         this.hide();
28359     }
28360     
28361 });
28362
28363  
28364  /*
28365  * - LGPL
28366  *
28367  * menu item
28368  * 
28369  */
28370 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28371
28372 /**
28373  * @class Roo.bootstrap.menu.Item
28374  * @extends Roo.bootstrap.Component
28375  * Bootstrap MenuItem class
28376  * @cfg {Boolean} submenu (true | false) default false
28377  * @cfg {String} html text of the item
28378  * @cfg {String} href the link
28379  * @cfg {Boolean} disable (true | false) default false
28380  * @cfg {Boolean} preventDefault (true | false) default true
28381  * @cfg {String} icon Font awesome icon
28382  * @cfg {String} pos Submenu align to (left | right) default right 
28383  * 
28384  * 
28385  * @constructor
28386  * Create a new Item
28387  * @param {Object} config The config object
28388  */
28389
28390
28391 Roo.bootstrap.menu.Item = function(config){
28392     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28393     this.addEvents({
28394         /**
28395          * @event mouseover
28396          * Fires when the mouse is hovering over this menu
28397          * @param {Roo.bootstrap.menu.Item} this
28398          * @param {Roo.EventObject} e
28399          */
28400         mouseover : true,
28401         /**
28402          * @event mouseout
28403          * Fires when the mouse exits this menu
28404          * @param {Roo.bootstrap.menu.Item} this
28405          * @param {Roo.EventObject} e
28406          */
28407         mouseout : true,
28408         // raw events
28409         /**
28410          * @event click
28411          * The raw click event for the entire grid.
28412          * @param {Roo.EventObject} e
28413          */
28414         click : true
28415     });
28416 };
28417
28418 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28419     
28420     submenu : false,
28421     href : '',
28422     html : '',
28423     preventDefault: true,
28424     disable : false,
28425     icon : false,
28426     pos : 'right',
28427     
28428     getAutoCreate : function()
28429     {
28430         var text = [
28431             {
28432                 tag : 'span',
28433                 cls : 'roo-menu-item-text',
28434                 html : this.html
28435             }
28436         ];
28437         
28438         if(this.icon){
28439             text.unshift({
28440                 tag : 'i',
28441                 cls : 'fa ' + this.icon
28442             })
28443         }
28444         
28445         var cfg = {
28446             tag : 'li',
28447             cn : [
28448                 {
28449                     tag : 'a',
28450                     href : this.href || '#',
28451                     cn : text
28452                 }
28453             ]
28454         };
28455         
28456         if(this.disable){
28457             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28458         }
28459         
28460         if(this.submenu){
28461             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28462             
28463             if(this.pos == 'left'){
28464                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28465             }
28466         }
28467         
28468         return cfg;
28469     },
28470     
28471     initEvents : function() 
28472     {
28473         this.el.on('mouseover', this.onMouseOver, this);
28474         this.el.on('mouseout', this.onMouseOut, this);
28475         
28476         this.el.select('a', true).first().on('click', this.onClick, this);
28477         
28478     },
28479     
28480     onClick : function(e)
28481     {
28482         if(this.preventDefault){
28483             e.preventDefault();
28484         }
28485         
28486         this.fireEvent("click", this, e);
28487     },
28488     
28489     onMouseOver : function(e)
28490     {
28491         if(this.submenu && this.pos == 'left'){
28492             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28493         }
28494         
28495         this.fireEvent("mouseover", this, e);
28496     },
28497     
28498     onMouseOut : function(e)
28499     {
28500         this.fireEvent("mouseout", this, e);
28501     }
28502 });
28503
28504  
28505
28506  /*
28507  * - LGPL
28508  *
28509  * menu separator
28510  * 
28511  */
28512 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28513
28514 /**
28515  * @class Roo.bootstrap.menu.Separator
28516  * @extends Roo.bootstrap.Component
28517  * Bootstrap Separator class
28518  * 
28519  * @constructor
28520  * Create a new Separator
28521  * @param {Object} config The config object
28522  */
28523
28524
28525 Roo.bootstrap.menu.Separator = function(config){
28526     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28527 };
28528
28529 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28530     
28531     getAutoCreate : function(){
28532         var cfg = {
28533             tag : 'li',
28534             cls: 'divider'
28535         };
28536         
28537         return cfg;
28538     }
28539    
28540 });
28541
28542  
28543
28544  /*
28545  * - LGPL
28546  *
28547  * Tooltip
28548  * 
28549  */
28550
28551 /**
28552  * @class Roo.bootstrap.Tooltip
28553  * Bootstrap Tooltip class
28554  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28555  * to determine which dom element triggers the tooltip.
28556  * 
28557  * It needs to add support for additional attributes like tooltip-position
28558  * 
28559  * @constructor
28560  * Create a new Toolti
28561  * @param {Object} config The config object
28562  */
28563
28564 Roo.bootstrap.Tooltip = function(config){
28565     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28566     
28567     this.alignment = Roo.bootstrap.Tooltip.alignment;
28568     
28569     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28570         this.alignment = config.alignment;
28571     }
28572     
28573 };
28574
28575 Roo.apply(Roo.bootstrap.Tooltip, {
28576     /**
28577      * @function init initialize tooltip monitoring.
28578      * @static
28579      */
28580     currentEl : false,
28581     currentTip : false,
28582     currentRegion : false,
28583     
28584     //  init : delay?
28585     
28586     init : function()
28587     {
28588         Roo.get(document).on('mouseover', this.enter ,this);
28589         Roo.get(document).on('mouseout', this.leave, this);
28590          
28591         
28592         this.currentTip = new Roo.bootstrap.Tooltip();
28593     },
28594     
28595     enter : function(ev)
28596     {
28597         var dom = ev.getTarget();
28598         
28599         //Roo.log(['enter',dom]);
28600         var el = Roo.fly(dom);
28601         if (this.currentEl) {
28602             //Roo.log(dom);
28603             //Roo.log(this.currentEl);
28604             //Roo.log(this.currentEl.contains(dom));
28605             if (this.currentEl == el) {
28606                 return;
28607             }
28608             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28609                 return;
28610             }
28611
28612         }
28613         
28614         if (this.currentTip.el) {
28615             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28616         }    
28617         //Roo.log(ev);
28618         
28619         if(!el || el.dom == document){
28620             return;
28621         }
28622         
28623         var bindEl = el;
28624         
28625         // you can not look for children, as if el is the body.. then everythign is the child..
28626         if (!el.attr('tooltip')) { //
28627             if (!el.select("[tooltip]").elements.length) {
28628                 return;
28629             }
28630             // is the mouse over this child...?
28631             bindEl = el.select("[tooltip]").first();
28632             var xy = ev.getXY();
28633             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28634                 //Roo.log("not in region.");
28635                 return;
28636             }
28637             //Roo.log("child element over..");
28638             
28639         }
28640         this.currentEl = bindEl;
28641         this.currentTip.bind(bindEl);
28642         this.currentRegion = Roo.lib.Region.getRegion(dom);
28643         this.currentTip.enter();
28644         
28645     },
28646     leave : function(ev)
28647     {
28648         var dom = ev.getTarget();
28649         //Roo.log(['leave',dom]);
28650         if (!this.currentEl) {
28651             return;
28652         }
28653         
28654         
28655         if (dom != this.currentEl.dom) {
28656             return;
28657         }
28658         var xy = ev.getXY();
28659         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28660             return;
28661         }
28662         // only activate leave if mouse cursor is outside... bounding box..
28663         
28664         
28665         
28666         
28667         if (this.currentTip) {
28668             this.currentTip.leave();
28669         }
28670         //Roo.log('clear currentEl');
28671         this.currentEl = false;
28672         
28673         
28674     },
28675     alignment : {
28676         'left' : ['r-l', [-2,0], 'right'],
28677         'right' : ['l-r', [2,0], 'left'],
28678         'bottom' : ['t-b', [0,2], 'top'],
28679         'top' : [ 'b-t', [0,-2], 'bottom']
28680     }
28681     
28682 });
28683
28684
28685 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28686     
28687     
28688     bindEl : false,
28689     
28690     delay : null, // can be { show : 300 , hide: 500}
28691     
28692     timeout : null,
28693     
28694     hoverState : null, //???
28695     
28696     placement : 'bottom', 
28697     
28698     alignment : false,
28699     
28700     getAutoCreate : function(){
28701     
28702         var cfg = {
28703            cls : 'tooltip',   
28704            role : 'tooltip',
28705            cn : [
28706                 {
28707                     cls : 'tooltip-arrow arrow'
28708                 },
28709                 {
28710                     cls : 'tooltip-inner'
28711                 }
28712            ]
28713         };
28714         
28715         return cfg;
28716     },
28717     bind : function(el)
28718     {
28719         this.bindEl = el;
28720     },
28721     
28722     initEvents : function()
28723     {
28724         this.arrowEl = this.el.select('.arrow', true).first();
28725         this.innerEl = this.el.select('.tooltip-inner', true).first();
28726     },
28727     
28728     enter : function () {
28729        
28730         if (this.timeout != null) {
28731             clearTimeout(this.timeout);
28732         }
28733         
28734         this.hoverState = 'in';
28735          //Roo.log("enter - show");
28736         if (!this.delay || !this.delay.show) {
28737             this.show();
28738             return;
28739         }
28740         var _t = this;
28741         this.timeout = setTimeout(function () {
28742             if (_t.hoverState == 'in') {
28743                 _t.show();
28744             }
28745         }, this.delay.show);
28746     },
28747     leave : function()
28748     {
28749         clearTimeout(this.timeout);
28750     
28751         this.hoverState = 'out';
28752          if (!this.delay || !this.delay.hide) {
28753             this.hide();
28754             return;
28755         }
28756        
28757         var _t = this;
28758         this.timeout = setTimeout(function () {
28759             //Roo.log("leave - timeout");
28760             
28761             if (_t.hoverState == 'out') {
28762                 _t.hide();
28763                 Roo.bootstrap.Tooltip.currentEl = false;
28764             }
28765         }, delay);
28766     },
28767     
28768     show : function (msg)
28769     {
28770         if (!this.el) {
28771             this.render(document.body);
28772         }
28773         // set content.
28774         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28775         
28776         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28777         
28778         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28779         
28780         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28781                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28782         
28783         var placement = typeof this.placement == 'function' ?
28784             this.placement.call(this, this.el, on_el) :
28785             this.placement;
28786             
28787         var autoToken = /\s?auto?\s?/i;
28788         var autoPlace = autoToken.test(placement);
28789         if (autoPlace) {
28790             placement = placement.replace(autoToken, '') || 'top';
28791         }
28792         
28793         //this.el.detach()
28794         //this.el.setXY([0,0]);
28795         this.el.show();
28796         //this.el.dom.style.display='block';
28797         
28798         //this.el.appendTo(on_el);
28799         
28800         var p = this.getPosition();
28801         var box = this.el.getBox();
28802         
28803         if (autoPlace) {
28804             // fixme..
28805         }
28806         
28807         var align = this.alignment[placement];
28808         
28809         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28810         
28811         if(placement == 'top' || placement == 'bottom'){
28812             if(xy[0] < 0){
28813                 placement = 'right';
28814             }
28815             
28816             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28817                 placement = 'left';
28818             }
28819             
28820             var scroll = Roo.select('body', true).first().getScroll();
28821             
28822             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28823                 placement = 'top';
28824             }
28825             
28826             align = this.alignment[placement];
28827             
28828             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28829             
28830         }
28831         
28832         this.el.alignTo(this.bindEl, align[0],align[1]);
28833         //var arrow = this.el.select('.arrow',true).first();
28834         //arrow.set(align[2], 
28835         
28836         this.el.addClass(placement);
28837         this.el.addClass("bs-tooltip-"+ placement);
28838         
28839         this.el.addClass('in fade show');
28840         
28841         this.hoverState = null;
28842         
28843         if (this.el.hasClass('fade')) {
28844             // fade it?
28845         }
28846         
28847         
28848         
28849         
28850         
28851     },
28852     hide : function()
28853     {
28854          
28855         if (!this.el) {
28856             return;
28857         }
28858         //this.el.setXY([0,0]);
28859         this.el.removeClass(['show', 'in']);
28860         //this.el.hide();
28861         
28862     }
28863     
28864 });
28865  
28866
28867  /*
28868  * - LGPL
28869  *
28870  * Location Picker
28871  * 
28872  */
28873
28874 /**
28875  * @class Roo.bootstrap.LocationPicker
28876  * @extends Roo.bootstrap.Component
28877  * Bootstrap LocationPicker class
28878  * @cfg {Number} latitude Position when init default 0
28879  * @cfg {Number} longitude Position when init default 0
28880  * @cfg {Number} zoom default 15
28881  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28882  * @cfg {Boolean} mapTypeControl default false
28883  * @cfg {Boolean} disableDoubleClickZoom default false
28884  * @cfg {Boolean} scrollwheel default true
28885  * @cfg {Boolean} streetViewControl default false
28886  * @cfg {Number} radius default 0
28887  * @cfg {String} locationName
28888  * @cfg {Boolean} draggable default true
28889  * @cfg {Boolean} enableAutocomplete default false
28890  * @cfg {Boolean} enableReverseGeocode default true
28891  * @cfg {String} markerTitle
28892  * 
28893  * @constructor
28894  * Create a new LocationPicker
28895  * @param {Object} config The config object
28896  */
28897
28898
28899 Roo.bootstrap.LocationPicker = function(config){
28900     
28901     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28902     
28903     this.addEvents({
28904         /**
28905          * @event initial
28906          * Fires when the picker initialized.
28907          * @param {Roo.bootstrap.LocationPicker} this
28908          * @param {Google Location} location
28909          */
28910         initial : true,
28911         /**
28912          * @event positionchanged
28913          * Fires when the picker position changed.
28914          * @param {Roo.bootstrap.LocationPicker} this
28915          * @param {Google Location} location
28916          */
28917         positionchanged : true,
28918         /**
28919          * @event resize
28920          * Fires when the map resize.
28921          * @param {Roo.bootstrap.LocationPicker} this
28922          */
28923         resize : true,
28924         /**
28925          * @event show
28926          * Fires when the map show.
28927          * @param {Roo.bootstrap.LocationPicker} this
28928          */
28929         show : true,
28930         /**
28931          * @event hide
28932          * Fires when the map hide.
28933          * @param {Roo.bootstrap.LocationPicker} this
28934          */
28935         hide : true,
28936         /**
28937          * @event mapClick
28938          * Fires when click the map.
28939          * @param {Roo.bootstrap.LocationPicker} this
28940          * @param {Map event} e
28941          */
28942         mapClick : true,
28943         /**
28944          * @event mapRightClick
28945          * Fires when right click the map.
28946          * @param {Roo.bootstrap.LocationPicker} this
28947          * @param {Map event} e
28948          */
28949         mapRightClick : true,
28950         /**
28951          * @event markerClick
28952          * Fires when click the marker.
28953          * @param {Roo.bootstrap.LocationPicker} this
28954          * @param {Map event} e
28955          */
28956         markerClick : true,
28957         /**
28958          * @event markerRightClick
28959          * Fires when right click the marker.
28960          * @param {Roo.bootstrap.LocationPicker} this
28961          * @param {Map event} e
28962          */
28963         markerRightClick : true,
28964         /**
28965          * @event OverlayViewDraw
28966          * Fires when OverlayView Draw
28967          * @param {Roo.bootstrap.LocationPicker} this
28968          */
28969         OverlayViewDraw : true,
28970         /**
28971          * @event OverlayViewOnAdd
28972          * Fires when OverlayView Draw
28973          * @param {Roo.bootstrap.LocationPicker} this
28974          */
28975         OverlayViewOnAdd : true,
28976         /**
28977          * @event OverlayViewOnRemove
28978          * Fires when OverlayView Draw
28979          * @param {Roo.bootstrap.LocationPicker} this
28980          */
28981         OverlayViewOnRemove : true,
28982         /**
28983          * @event OverlayViewShow
28984          * Fires when OverlayView Draw
28985          * @param {Roo.bootstrap.LocationPicker} this
28986          * @param {Pixel} cpx
28987          */
28988         OverlayViewShow : true,
28989         /**
28990          * @event OverlayViewHide
28991          * Fires when OverlayView Draw
28992          * @param {Roo.bootstrap.LocationPicker} this
28993          */
28994         OverlayViewHide : true,
28995         /**
28996          * @event loadexception
28997          * Fires when load google lib failed.
28998          * @param {Roo.bootstrap.LocationPicker} this
28999          */
29000         loadexception : true
29001     });
29002         
29003 };
29004
29005 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29006     
29007     gMapContext: false,
29008     
29009     latitude: 0,
29010     longitude: 0,
29011     zoom: 15,
29012     mapTypeId: false,
29013     mapTypeControl: false,
29014     disableDoubleClickZoom: false,
29015     scrollwheel: true,
29016     streetViewControl: false,
29017     radius: 0,
29018     locationName: '',
29019     draggable: true,
29020     enableAutocomplete: false,
29021     enableReverseGeocode: true,
29022     markerTitle: '',
29023     
29024     getAutoCreate: function()
29025     {
29026
29027         var cfg = {
29028             tag: 'div',
29029             cls: 'roo-location-picker'
29030         };
29031         
29032         return cfg
29033     },
29034     
29035     initEvents: function(ct, position)
29036     {       
29037         if(!this.el.getWidth() || this.isApplied()){
29038             return;
29039         }
29040         
29041         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29042         
29043         this.initial();
29044     },
29045     
29046     initial: function()
29047     {
29048         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29049             this.fireEvent('loadexception', this);
29050             return;
29051         }
29052         
29053         if(!this.mapTypeId){
29054             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29055         }
29056         
29057         this.gMapContext = this.GMapContext();
29058         
29059         this.initOverlayView();
29060         
29061         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29062         
29063         var _this = this;
29064                 
29065         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29066             _this.setPosition(_this.gMapContext.marker.position);
29067         });
29068         
29069         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29070             _this.fireEvent('mapClick', this, event);
29071             
29072         });
29073
29074         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29075             _this.fireEvent('mapRightClick', this, event);
29076             
29077         });
29078         
29079         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29080             _this.fireEvent('markerClick', this, event);
29081             
29082         });
29083
29084         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29085             _this.fireEvent('markerRightClick', this, event);
29086             
29087         });
29088         
29089         this.setPosition(this.gMapContext.location);
29090         
29091         this.fireEvent('initial', this, this.gMapContext.location);
29092     },
29093     
29094     initOverlayView: function()
29095     {
29096         var _this = this;
29097         
29098         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29099             
29100             draw: function()
29101             {
29102                 _this.fireEvent('OverlayViewDraw', _this);
29103             },
29104             
29105             onAdd: function()
29106             {
29107                 _this.fireEvent('OverlayViewOnAdd', _this);
29108             },
29109             
29110             onRemove: function()
29111             {
29112                 _this.fireEvent('OverlayViewOnRemove', _this);
29113             },
29114             
29115             show: function(cpx)
29116             {
29117                 _this.fireEvent('OverlayViewShow', _this, cpx);
29118             },
29119             
29120             hide: function()
29121             {
29122                 _this.fireEvent('OverlayViewHide', _this);
29123             }
29124             
29125         });
29126     },
29127     
29128     fromLatLngToContainerPixel: function(event)
29129     {
29130         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29131     },
29132     
29133     isApplied: function() 
29134     {
29135         return this.getGmapContext() == false ? false : true;
29136     },
29137     
29138     getGmapContext: function() 
29139     {
29140         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29141     },
29142     
29143     GMapContext: function() 
29144     {
29145         var position = new google.maps.LatLng(this.latitude, this.longitude);
29146         
29147         var _map = new google.maps.Map(this.el.dom, {
29148             center: position,
29149             zoom: this.zoom,
29150             mapTypeId: this.mapTypeId,
29151             mapTypeControl: this.mapTypeControl,
29152             disableDoubleClickZoom: this.disableDoubleClickZoom,
29153             scrollwheel: this.scrollwheel,
29154             streetViewControl: this.streetViewControl,
29155             locationName: this.locationName,
29156             draggable: this.draggable,
29157             enableAutocomplete: this.enableAutocomplete,
29158             enableReverseGeocode: this.enableReverseGeocode
29159         });
29160         
29161         var _marker = new google.maps.Marker({
29162             position: position,
29163             map: _map,
29164             title: this.markerTitle,
29165             draggable: this.draggable
29166         });
29167         
29168         return {
29169             map: _map,
29170             marker: _marker,
29171             circle: null,
29172             location: position,
29173             radius: this.radius,
29174             locationName: this.locationName,
29175             addressComponents: {
29176                 formatted_address: null,
29177                 addressLine1: null,
29178                 addressLine2: null,
29179                 streetName: null,
29180                 streetNumber: null,
29181                 city: null,
29182                 district: null,
29183                 state: null,
29184                 stateOrProvince: null
29185             },
29186             settings: this,
29187             domContainer: this.el.dom,
29188             geodecoder: new google.maps.Geocoder()
29189         };
29190     },
29191     
29192     drawCircle: function(center, radius, options) 
29193     {
29194         if (this.gMapContext.circle != null) {
29195             this.gMapContext.circle.setMap(null);
29196         }
29197         if (radius > 0) {
29198             radius *= 1;
29199             options = Roo.apply({}, options, {
29200                 strokeColor: "#0000FF",
29201                 strokeOpacity: .35,
29202                 strokeWeight: 2,
29203                 fillColor: "#0000FF",
29204                 fillOpacity: .2
29205             });
29206             
29207             options.map = this.gMapContext.map;
29208             options.radius = radius;
29209             options.center = center;
29210             this.gMapContext.circle = new google.maps.Circle(options);
29211             return this.gMapContext.circle;
29212         }
29213         
29214         return null;
29215     },
29216     
29217     setPosition: function(location) 
29218     {
29219         this.gMapContext.location = location;
29220         this.gMapContext.marker.setPosition(location);
29221         this.gMapContext.map.panTo(location);
29222         this.drawCircle(location, this.gMapContext.radius, {});
29223         
29224         var _this = this;
29225         
29226         if (this.gMapContext.settings.enableReverseGeocode) {
29227             this.gMapContext.geodecoder.geocode({
29228                 latLng: this.gMapContext.location
29229             }, function(results, status) {
29230                 
29231                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29232                     _this.gMapContext.locationName = results[0].formatted_address;
29233                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29234                     
29235                     _this.fireEvent('positionchanged', this, location);
29236                 }
29237             });
29238             
29239             return;
29240         }
29241         
29242         this.fireEvent('positionchanged', this, location);
29243     },
29244     
29245     resize: function()
29246     {
29247         google.maps.event.trigger(this.gMapContext.map, "resize");
29248         
29249         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29250         
29251         this.fireEvent('resize', this);
29252     },
29253     
29254     setPositionByLatLng: function(latitude, longitude)
29255     {
29256         this.setPosition(new google.maps.LatLng(latitude, longitude));
29257     },
29258     
29259     getCurrentPosition: function() 
29260     {
29261         return {
29262             latitude: this.gMapContext.location.lat(),
29263             longitude: this.gMapContext.location.lng()
29264         };
29265     },
29266     
29267     getAddressName: function() 
29268     {
29269         return this.gMapContext.locationName;
29270     },
29271     
29272     getAddressComponents: function() 
29273     {
29274         return this.gMapContext.addressComponents;
29275     },
29276     
29277     address_component_from_google_geocode: function(address_components) 
29278     {
29279         var result = {};
29280         
29281         for (var i = 0; i < address_components.length; i++) {
29282             var component = address_components[i];
29283             if (component.types.indexOf("postal_code") >= 0) {
29284                 result.postalCode = component.short_name;
29285             } else if (component.types.indexOf("street_number") >= 0) {
29286                 result.streetNumber = component.short_name;
29287             } else if (component.types.indexOf("route") >= 0) {
29288                 result.streetName = component.short_name;
29289             } else if (component.types.indexOf("neighborhood") >= 0) {
29290                 result.city = component.short_name;
29291             } else if (component.types.indexOf("locality") >= 0) {
29292                 result.city = component.short_name;
29293             } else if (component.types.indexOf("sublocality") >= 0) {
29294                 result.district = component.short_name;
29295             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29296                 result.stateOrProvince = component.short_name;
29297             } else if (component.types.indexOf("country") >= 0) {
29298                 result.country = component.short_name;
29299             }
29300         }
29301         
29302         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29303         result.addressLine2 = "";
29304         return result;
29305     },
29306     
29307     setZoomLevel: function(zoom)
29308     {
29309         this.gMapContext.map.setZoom(zoom);
29310     },
29311     
29312     show: function()
29313     {
29314         if(!this.el){
29315             return;
29316         }
29317         
29318         this.el.show();
29319         
29320         this.resize();
29321         
29322         this.fireEvent('show', this);
29323     },
29324     
29325     hide: function()
29326     {
29327         if(!this.el){
29328             return;
29329         }
29330         
29331         this.el.hide();
29332         
29333         this.fireEvent('hide', this);
29334     }
29335     
29336 });
29337
29338 Roo.apply(Roo.bootstrap.LocationPicker, {
29339     
29340     OverlayView : function(map, options)
29341     {
29342         options = options || {};
29343         
29344         this.setMap(map);
29345     }
29346     
29347     
29348 });/**
29349  * @class Roo.bootstrap.Alert
29350  * @extends Roo.bootstrap.Component
29351  * Bootstrap Alert class - shows an alert area box
29352  * eg
29353  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29354   Enter a valid email address
29355 </div>
29356  * @licence LGPL
29357  * @cfg {String} title The title of alert
29358  * @cfg {String} html The content of alert
29359  * @cfg {String} weight (  success | info | warning | danger )
29360  * @cfg {String} faicon font-awesomeicon
29361  * 
29362  * @constructor
29363  * Create a new alert
29364  * @param {Object} config The config object
29365  */
29366
29367
29368 Roo.bootstrap.Alert = function(config){
29369     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29370     
29371 };
29372
29373 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29374     
29375     title: '',
29376     html: '',
29377     weight: false,
29378     faicon: false,
29379     
29380     getAutoCreate : function()
29381     {
29382         
29383         var cfg = {
29384             tag : 'div',
29385             cls : 'alert',
29386             cn : [
29387                 {
29388                     tag : 'i',
29389                     cls : 'roo-alert-icon'
29390                     
29391                 },
29392                 {
29393                     tag : 'b',
29394                     cls : 'roo-alert-title',
29395                     html : this.title
29396                 },
29397                 {
29398                     tag : 'span',
29399                     cls : 'roo-alert-text',
29400                     html : this.html
29401                 }
29402             ]
29403         };
29404         
29405         if(this.faicon){
29406             cfg.cn[0].cls += ' fa ' + this.faicon;
29407         }
29408         
29409         if(this.weight){
29410             cfg.cls += ' alert-' + this.weight;
29411         }
29412         
29413         return cfg;
29414     },
29415     
29416     initEvents: function() 
29417     {
29418         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29419     },
29420     
29421     setTitle : function(str)
29422     {
29423         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29424     },
29425     
29426     setText : function(str)
29427     {
29428         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29429     },
29430     
29431     setWeight : function(weight)
29432     {
29433         if(this.weight){
29434             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29435         }
29436         
29437         this.weight = weight;
29438         
29439         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29440     },
29441     
29442     setIcon : function(icon)
29443     {
29444         if(this.faicon){
29445             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29446         }
29447         
29448         this.faicon = icon;
29449         
29450         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29451     },
29452     
29453     hide: function() 
29454     {
29455         this.el.hide();   
29456     },
29457     
29458     show: function() 
29459     {  
29460         this.el.show();   
29461     }
29462     
29463 });
29464
29465  
29466 /*
29467 * Licence: LGPL
29468 */
29469
29470 /**
29471  * @class Roo.bootstrap.UploadCropbox
29472  * @extends Roo.bootstrap.Component
29473  * Bootstrap UploadCropbox class
29474  * @cfg {String} emptyText show when image has been loaded
29475  * @cfg {String} rotateNotify show when image too small to rotate
29476  * @cfg {Number} errorTimeout default 3000
29477  * @cfg {Number} minWidth default 300
29478  * @cfg {Number} minHeight default 300
29479  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29480  * @cfg {Boolean} isDocument (true|false) default false
29481  * @cfg {String} url action url
29482  * @cfg {String} paramName default 'imageUpload'
29483  * @cfg {String} method default POST
29484  * @cfg {Boolean} loadMask (true|false) default true
29485  * @cfg {Boolean} loadingText default 'Loading...'
29486  * 
29487  * @constructor
29488  * Create a new UploadCropbox
29489  * @param {Object} config The config object
29490  */
29491
29492 Roo.bootstrap.UploadCropbox = function(config){
29493     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29494     
29495     this.addEvents({
29496         /**
29497          * @event beforeselectfile
29498          * Fire before select file
29499          * @param {Roo.bootstrap.UploadCropbox} this
29500          */
29501         "beforeselectfile" : true,
29502         /**
29503          * @event initial
29504          * Fire after initEvent
29505          * @param {Roo.bootstrap.UploadCropbox} this
29506          */
29507         "initial" : true,
29508         /**
29509          * @event crop
29510          * Fire after initEvent
29511          * @param {Roo.bootstrap.UploadCropbox} this
29512          * @param {String} data
29513          */
29514         "crop" : true,
29515         /**
29516          * @event prepare
29517          * Fire when preparing the file data
29518          * @param {Roo.bootstrap.UploadCropbox} this
29519          * @param {Object} file
29520          */
29521         "prepare" : true,
29522         /**
29523          * @event exception
29524          * Fire when get exception
29525          * @param {Roo.bootstrap.UploadCropbox} this
29526          * @param {XMLHttpRequest} xhr
29527          */
29528         "exception" : true,
29529         /**
29530          * @event beforeloadcanvas
29531          * Fire before load the canvas
29532          * @param {Roo.bootstrap.UploadCropbox} this
29533          * @param {String} src
29534          */
29535         "beforeloadcanvas" : true,
29536         /**
29537          * @event trash
29538          * Fire when trash image
29539          * @param {Roo.bootstrap.UploadCropbox} this
29540          */
29541         "trash" : true,
29542         /**
29543          * @event download
29544          * Fire when download the image
29545          * @param {Roo.bootstrap.UploadCropbox} this
29546          */
29547         "download" : true,
29548         /**
29549          * @event footerbuttonclick
29550          * Fire when footerbuttonclick
29551          * @param {Roo.bootstrap.UploadCropbox} this
29552          * @param {String} type
29553          */
29554         "footerbuttonclick" : true,
29555         /**
29556          * @event resize
29557          * Fire when resize
29558          * @param {Roo.bootstrap.UploadCropbox} this
29559          */
29560         "resize" : true,
29561         /**
29562          * @event rotate
29563          * Fire when rotate the image
29564          * @param {Roo.bootstrap.UploadCropbox} this
29565          * @param {String} pos
29566          */
29567         "rotate" : true,
29568         /**
29569          * @event inspect
29570          * Fire when inspect the file
29571          * @param {Roo.bootstrap.UploadCropbox} this
29572          * @param {Object} file
29573          */
29574         "inspect" : true,
29575         /**
29576          * @event upload
29577          * Fire when xhr upload the file
29578          * @param {Roo.bootstrap.UploadCropbox} this
29579          * @param {Object} data
29580          */
29581         "upload" : true,
29582         /**
29583          * @event arrange
29584          * Fire when arrange the file data
29585          * @param {Roo.bootstrap.UploadCropbox} this
29586          * @param {Object} formData
29587          */
29588         "arrange" : true
29589     });
29590     
29591     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29592 };
29593
29594 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29595     
29596     emptyText : 'Click to upload image',
29597     rotateNotify : 'Image is too small to rotate',
29598     errorTimeout : 3000,
29599     scale : 0,
29600     baseScale : 1,
29601     rotate : 0,
29602     dragable : false,
29603     pinching : false,
29604     mouseX : 0,
29605     mouseY : 0,
29606     cropData : false,
29607     minWidth : 300,
29608     minHeight : 300,
29609     file : false,
29610     exif : {},
29611     baseRotate : 1,
29612     cropType : 'image/jpeg',
29613     buttons : false,
29614     canvasLoaded : false,
29615     isDocument : false,
29616     method : 'POST',
29617     paramName : 'imageUpload',
29618     loadMask : true,
29619     loadingText : 'Loading...',
29620     maskEl : false,
29621     
29622     getAutoCreate : function()
29623     {
29624         var cfg = {
29625             tag : 'div',
29626             cls : 'roo-upload-cropbox',
29627             cn : [
29628                 {
29629                     tag : 'input',
29630                     cls : 'roo-upload-cropbox-selector',
29631                     type : 'file'
29632                 },
29633                 {
29634                     tag : 'div',
29635                     cls : 'roo-upload-cropbox-body',
29636                     style : 'cursor:pointer',
29637                     cn : [
29638                         {
29639                             tag : 'div',
29640                             cls : 'roo-upload-cropbox-preview'
29641                         },
29642                         {
29643                             tag : 'div',
29644                             cls : 'roo-upload-cropbox-thumb'
29645                         },
29646                         {
29647                             tag : 'div',
29648                             cls : 'roo-upload-cropbox-empty-notify',
29649                             html : this.emptyText
29650                         },
29651                         {
29652                             tag : 'div',
29653                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29654                             html : this.rotateNotify
29655                         }
29656                     ]
29657                 },
29658                 {
29659                     tag : 'div',
29660                     cls : 'roo-upload-cropbox-footer',
29661                     cn : {
29662                         tag : 'div',
29663                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29664                         cn : []
29665                     }
29666                 }
29667             ]
29668         };
29669         
29670         return cfg;
29671     },
29672     
29673     onRender : function(ct, position)
29674     {
29675         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29676         
29677         if (this.buttons.length) {
29678             
29679             Roo.each(this.buttons, function(bb) {
29680                 
29681                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29682                 
29683                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29684                 
29685             }, this);
29686         }
29687         
29688         if(this.loadMask){
29689             this.maskEl = this.el;
29690         }
29691     },
29692     
29693     initEvents : function()
29694     {
29695         this.urlAPI = (window.createObjectURL && window) || 
29696                                 (window.URL && URL.revokeObjectURL && URL) || 
29697                                 (window.webkitURL && webkitURL);
29698                         
29699         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29700         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29701         
29702         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29703         this.selectorEl.hide();
29704         
29705         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29706         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29707         
29708         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29709         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29710         this.thumbEl.hide();
29711         
29712         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29713         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29714         
29715         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29716         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29717         this.errorEl.hide();
29718         
29719         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29720         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29721         this.footerEl.hide();
29722         
29723         this.setThumbBoxSize();
29724         
29725         this.bind();
29726         
29727         this.resize();
29728         
29729         this.fireEvent('initial', this);
29730     },
29731
29732     bind : function()
29733     {
29734         var _this = this;
29735         
29736         window.addEventListener("resize", function() { _this.resize(); } );
29737         
29738         this.bodyEl.on('click', this.beforeSelectFile, this);
29739         
29740         if(Roo.isTouch){
29741             this.bodyEl.on('touchstart', this.onTouchStart, this);
29742             this.bodyEl.on('touchmove', this.onTouchMove, this);
29743             this.bodyEl.on('touchend', this.onTouchEnd, this);
29744         }
29745         
29746         if(!Roo.isTouch){
29747             this.bodyEl.on('mousedown', this.onMouseDown, this);
29748             this.bodyEl.on('mousemove', this.onMouseMove, this);
29749             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29750             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29751             Roo.get(document).on('mouseup', this.onMouseUp, this);
29752         }
29753         
29754         this.selectorEl.on('change', this.onFileSelected, this);
29755     },
29756     
29757     reset : function()
29758     {    
29759         this.scale = 0;
29760         this.baseScale = 1;
29761         this.rotate = 0;
29762         this.baseRotate = 1;
29763         this.dragable = false;
29764         this.pinching = false;
29765         this.mouseX = 0;
29766         this.mouseY = 0;
29767         this.cropData = false;
29768         this.notifyEl.dom.innerHTML = this.emptyText;
29769         
29770         this.selectorEl.dom.value = '';
29771         
29772     },
29773     
29774     resize : function()
29775     {
29776         if(this.fireEvent('resize', this) != false){
29777             this.setThumbBoxPosition();
29778             this.setCanvasPosition();
29779         }
29780     },
29781     
29782     onFooterButtonClick : function(e, el, o, type)
29783     {
29784         switch (type) {
29785             case 'rotate-left' :
29786                 this.onRotateLeft(e);
29787                 break;
29788             case 'rotate-right' :
29789                 this.onRotateRight(e);
29790                 break;
29791             case 'picture' :
29792                 this.beforeSelectFile(e);
29793                 break;
29794             case 'trash' :
29795                 this.trash(e);
29796                 break;
29797             case 'crop' :
29798                 this.crop(e);
29799                 break;
29800             case 'download' :
29801                 this.download(e);
29802                 break;
29803             default :
29804                 break;
29805         }
29806         
29807         this.fireEvent('footerbuttonclick', this, type);
29808     },
29809     
29810     beforeSelectFile : function(e)
29811     {
29812         e.preventDefault();
29813         
29814         if(this.fireEvent('beforeselectfile', this) != false){
29815             this.selectorEl.dom.click();
29816         }
29817     },
29818     
29819     onFileSelected : function(e)
29820     {
29821         e.preventDefault();
29822         
29823         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29824             return;
29825         }
29826         
29827         var file = this.selectorEl.dom.files[0];
29828         
29829         if(this.fireEvent('inspect', this, file) != false){
29830             this.prepare(file);
29831         }
29832         
29833     },
29834     
29835     trash : function(e)
29836     {
29837         this.fireEvent('trash', this);
29838     },
29839     
29840     download : function(e)
29841     {
29842         this.fireEvent('download', this);
29843     },
29844     
29845     loadCanvas : function(src)
29846     {   
29847         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29848             
29849             this.reset();
29850             
29851             this.imageEl = document.createElement('img');
29852             
29853             var _this = this;
29854             
29855             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29856             
29857             this.imageEl.src = src;
29858         }
29859     },
29860     
29861     onLoadCanvas : function()
29862     {   
29863         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29864         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29865         
29866         this.bodyEl.un('click', this.beforeSelectFile, this);
29867         
29868         this.notifyEl.hide();
29869         this.thumbEl.show();
29870         this.footerEl.show();
29871         
29872         this.baseRotateLevel();
29873         
29874         if(this.isDocument){
29875             this.setThumbBoxSize();
29876         }
29877         
29878         this.setThumbBoxPosition();
29879         
29880         this.baseScaleLevel();
29881         
29882         this.draw();
29883         
29884         this.resize();
29885         
29886         this.canvasLoaded = true;
29887         
29888         if(this.loadMask){
29889             this.maskEl.unmask();
29890         }
29891         
29892     },
29893     
29894     setCanvasPosition : function()
29895     {   
29896         if(!this.canvasEl){
29897             return;
29898         }
29899         
29900         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29901         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29902         
29903         this.previewEl.setLeft(pw);
29904         this.previewEl.setTop(ph);
29905         
29906     },
29907     
29908     onMouseDown : function(e)
29909     {   
29910         e.stopEvent();
29911         
29912         this.dragable = true;
29913         this.pinching = false;
29914         
29915         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29916             this.dragable = false;
29917             return;
29918         }
29919         
29920         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29921         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29922         
29923     },
29924     
29925     onMouseMove : function(e)
29926     {   
29927         e.stopEvent();
29928         
29929         if(!this.canvasLoaded){
29930             return;
29931         }
29932         
29933         if (!this.dragable){
29934             return;
29935         }
29936         
29937         var minX = Math.ceil(this.thumbEl.getLeft(true));
29938         var minY = Math.ceil(this.thumbEl.getTop(true));
29939         
29940         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29941         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29942         
29943         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29944         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29945         
29946         x = x - this.mouseX;
29947         y = y - this.mouseY;
29948         
29949         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29950         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29951         
29952         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29953         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29954         
29955         this.previewEl.setLeft(bgX);
29956         this.previewEl.setTop(bgY);
29957         
29958         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29959         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29960     },
29961     
29962     onMouseUp : function(e)
29963     {   
29964         e.stopEvent();
29965         
29966         this.dragable = false;
29967     },
29968     
29969     onMouseWheel : function(e)
29970     {   
29971         e.stopEvent();
29972         
29973         this.startScale = this.scale;
29974         
29975         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29976         
29977         if(!this.zoomable()){
29978             this.scale = this.startScale;
29979             return;
29980         }
29981         
29982         this.draw();
29983         
29984         return;
29985     },
29986     
29987     zoomable : function()
29988     {
29989         var minScale = this.thumbEl.getWidth() / this.minWidth;
29990         
29991         if(this.minWidth < this.minHeight){
29992             minScale = this.thumbEl.getHeight() / this.minHeight;
29993         }
29994         
29995         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29996         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29997         
29998         if(
29999                 this.isDocument &&
30000                 (this.rotate == 0 || this.rotate == 180) && 
30001                 (
30002                     width > this.imageEl.OriginWidth || 
30003                     height > this.imageEl.OriginHeight ||
30004                     (width < this.minWidth && height < this.minHeight)
30005                 )
30006         ){
30007             return false;
30008         }
30009         
30010         if(
30011                 this.isDocument &&
30012                 (this.rotate == 90 || this.rotate == 270) && 
30013                 (
30014                     width > this.imageEl.OriginWidth || 
30015                     height > this.imageEl.OriginHeight ||
30016                     (width < this.minHeight && height < this.minWidth)
30017                 )
30018         ){
30019             return false;
30020         }
30021         
30022         if(
30023                 !this.isDocument &&
30024                 (this.rotate == 0 || this.rotate == 180) && 
30025                 (
30026                     width < this.minWidth || 
30027                     width > this.imageEl.OriginWidth || 
30028                     height < this.minHeight || 
30029                     height > this.imageEl.OriginHeight
30030                 )
30031         ){
30032             return false;
30033         }
30034         
30035         if(
30036                 !this.isDocument &&
30037                 (this.rotate == 90 || this.rotate == 270) && 
30038                 (
30039                     width < this.minHeight || 
30040                     width > this.imageEl.OriginWidth || 
30041                     height < this.minWidth || 
30042                     height > this.imageEl.OriginHeight
30043                 )
30044         ){
30045             return false;
30046         }
30047         
30048         return true;
30049         
30050     },
30051     
30052     onRotateLeft : function(e)
30053     {   
30054         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30055             
30056             var minScale = this.thumbEl.getWidth() / this.minWidth;
30057             
30058             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30059             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30060             
30061             this.startScale = this.scale;
30062             
30063             while (this.getScaleLevel() < minScale){
30064             
30065                 this.scale = this.scale + 1;
30066                 
30067                 if(!this.zoomable()){
30068                     break;
30069                 }
30070                 
30071                 if(
30072                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30073                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30074                 ){
30075                     continue;
30076                 }
30077                 
30078                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30079
30080                 this.draw();
30081                 
30082                 return;
30083             }
30084             
30085             this.scale = this.startScale;
30086             
30087             this.onRotateFail();
30088             
30089             return false;
30090         }
30091         
30092         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30093
30094         if(this.isDocument){
30095             this.setThumbBoxSize();
30096             this.setThumbBoxPosition();
30097             this.setCanvasPosition();
30098         }
30099         
30100         this.draw();
30101         
30102         this.fireEvent('rotate', this, 'left');
30103         
30104     },
30105     
30106     onRotateRight : function(e)
30107     {
30108         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30109             
30110             var minScale = this.thumbEl.getWidth() / this.minWidth;
30111         
30112             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30113             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30114             
30115             this.startScale = this.scale;
30116             
30117             while (this.getScaleLevel() < minScale){
30118             
30119                 this.scale = this.scale + 1;
30120                 
30121                 if(!this.zoomable()){
30122                     break;
30123                 }
30124                 
30125                 if(
30126                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30127                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30128                 ){
30129                     continue;
30130                 }
30131                 
30132                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30133
30134                 this.draw();
30135                 
30136                 return;
30137             }
30138             
30139             this.scale = this.startScale;
30140             
30141             this.onRotateFail();
30142             
30143             return false;
30144         }
30145         
30146         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30147
30148         if(this.isDocument){
30149             this.setThumbBoxSize();
30150             this.setThumbBoxPosition();
30151             this.setCanvasPosition();
30152         }
30153         
30154         this.draw();
30155         
30156         this.fireEvent('rotate', this, 'right');
30157     },
30158     
30159     onRotateFail : function()
30160     {
30161         this.errorEl.show(true);
30162         
30163         var _this = this;
30164         
30165         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30166     },
30167     
30168     draw : function()
30169     {
30170         this.previewEl.dom.innerHTML = '';
30171         
30172         var canvasEl = document.createElement("canvas");
30173         
30174         var contextEl = canvasEl.getContext("2d");
30175         
30176         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30177         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30178         var center = this.imageEl.OriginWidth / 2;
30179         
30180         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30181             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30182             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30183             center = this.imageEl.OriginHeight / 2;
30184         }
30185         
30186         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30187         
30188         contextEl.translate(center, center);
30189         contextEl.rotate(this.rotate * Math.PI / 180);
30190
30191         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30192         
30193         this.canvasEl = document.createElement("canvas");
30194         
30195         this.contextEl = this.canvasEl.getContext("2d");
30196         
30197         switch (this.rotate) {
30198             case 0 :
30199                 
30200                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30201                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30202                 
30203                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30204                 
30205                 break;
30206             case 90 : 
30207                 
30208                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30209                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30210                 
30211                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30212                     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);
30213                     break;
30214                 }
30215                 
30216                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30217                 
30218                 break;
30219             case 180 :
30220                 
30221                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30222                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30223                 
30224                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30225                     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);
30226                     break;
30227                 }
30228                 
30229                 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);
30230                 
30231                 break;
30232             case 270 :
30233                 
30234                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30235                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30236         
30237                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30238                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30239                     break;
30240                 }
30241                 
30242                 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);
30243                 
30244                 break;
30245             default : 
30246                 break;
30247         }
30248         
30249         this.previewEl.appendChild(this.canvasEl);
30250         
30251         this.setCanvasPosition();
30252     },
30253     
30254     crop : function()
30255     {
30256         if(!this.canvasLoaded){
30257             return;
30258         }
30259         
30260         var imageCanvas = document.createElement("canvas");
30261         
30262         var imageContext = imageCanvas.getContext("2d");
30263         
30264         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30265         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30266         
30267         var center = imageCanvas.width / 2;
30268         
30269         imageContext.translate(center, center);
30270         
30271         imageContext.rotate(this.rotate * Math.PI / 180);
30272         
30273         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30274         
30275         var canvas = document.createElement("canvas");
30276         
30277         var context = canvas.getContext("2d");
30278                 
30279         canvas.width = this.minWidth;
30280         canvas.height = this.minHeight;
30281
30282         switch (this.rotate) {
30283             case 0 :
30284                 
30285                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30286                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30287                 
30288                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30289                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30290                 
30291                 var targetWidth = this.minWidth - 2 * x;
30292                 var targetHeight = this.minHeight - 2 * y;
30293                 
30294                 var scale = 1;
30295                 
30296                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30297                     scale = targetWidth / width;
30298                 }
30299                 
30300                 if(x > 0 && y == 0){
30301                     scale = targetHeight / height;
30302                 }
30303                 
30304                 if(x > 0 && y > 0){
30305                     scale = targetWidth / width;
30306                     
30307                     if(width < height){
30308                         scale = targetHeight / height;
30309                     }
30310                 }
30311                 
30312                 context.scale(scale, scale);
30313                 
30314                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30315                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30316
30317                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30318                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30319
30320                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30321                 
30322                 break;
30323             case 90 : 
30324                 
30325                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30326                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30327                 
30328                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30329                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30330                 
30331                 var targetWidth = this.minWidth - 2 * x;
30332                 var targetHeight = this.minHeight - 2 * y;
30333                 
30334                 var scale = 1;
30335                 
30336                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30337                     scale = targetWidth / width;
30338                 }
30339                 
30340                 if(x > 0 && y == 0){
30341                     scale = targetHeight / height;
30342                 }
30343                 
30344                 if(x > 0 && y > 0){
30345                     scale = targetWidth / width;
30346                     
30347                     if(width < height){
30348                         scale = targetHeight / height;
30349                     }
30350                 }
30351                 
30352                 context.scale(scale, scale);
30353                 
30354                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30355                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30356
30357                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30358                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30359                 
30360                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30361                 
30362                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30363                 
30364                 break;
30365             case 180 :
30366                 
30367                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30368                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30369                 
30370                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30371                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30372                 
30373                 var targetWidth = this.minWidth - 2 * x;
30374                 var targetHeight = this.minHeight - 2 * y;
30375                 
30376                 var scale = 1;
30377                 
30378                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30379                     scale = targetWidth / width;
30380                 }
30381                 
30382                 if(x > 0 && y == 0){
30383                     scale = targetHeight / height;
30384                 }
30385                 
30386                 if(x > 0 && y > 0){
30387                     scale = targetWidth / width;
30388                     
30389                     if(width < height){
30390                         scale = targetHeight / height;
30391                     }
30392                 }
30393                 
30394                 context.scale(scale, scale);
30395                 
30396                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30397                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30398
30399                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30400                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30401
30402                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30403                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30404                 
30405                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30406                 
30407                 break;
30408             case 270 :
30409                 
30410                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30411                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30412                 
30413                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30414                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30415                 
30416                 var targetWidth = this.minWidth - 2 * x;
30417                 var targetHeight = this.minHeight - 2 * y;
30418                 
30419                 var scale = 1;
30420                 
30421                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30422                     scale = targetWidth / width;
30423                 }
30424                 
30425                 if(x > 0 && y == 0){
30426                     scale = targetHeight / height;
30427                 }
30428                 
30429                 if(x > 0 && y > 0){
30430                     scale = targetWidth / width;
30431                     
30432                     if(width < height){
30433                         scale = targetHeight / height;
30434                     }
30435                 }
30436                 
30437                 context.scale(scale, scale);
30438                 
30439                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30440                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30441
30442                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30443                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30444                 
30445                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30446                 
30447                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30448                 
30449                 break;
30450             default : 
30451                 break;
30452         }
30453         
30454         this.cropData = canvas.toDataURL(this.cropType);
30455         
30456         if(this.fireEvent('crop', this, this.cropData) !== false){
30457             this.process(this.file, this.cropData);
30458         }
30459         
30460         return;
30461         
30462     },
30463     
30464     setThumbBoxSize : function()
30465     {
30466         var width, height;
30467         
30468         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30469             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30470             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30471             
30472             this.minWidth = width;
30473             this.minHeight = height;
30474             
30475             if(this.rotate == 90 || this.rotate == 270){
30476                 this.minWidth = height;
30477                 this.minHeight = width;
30478             }
30479         }
30480         
30481         height = 300;
30482         width = Math.ceil(this.minWidth * height / this.minHeight);
30483         
30484         if(this.minWidth > this.minHeight){
30485             width = 300;
30486             height = Math.ceil(this.minHeight * width / this.minWidth);
30487         }
30488         
30489         this.thumbEl.setStyle({
30490             width : width + 'px',
30491             height : height + 'px'
30492         });
30493
30494         return;
30495             
30496     },
30497     
30498     setThumbBoxPosition : function()
30499     {
30500         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30501         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30502         
30503         this.thumbEl.setLeft(x);
30504         this.thumbEl.setTop(y);
30505         
30506     },
30507     
30508     baseRotateLevel : function()
30509     {
30510         this.baseRotate = 1;
30511         
30512         if(
30513                 typeof(this.exif) != 'undefined' &&
30514                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30515                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30516         ){
30517             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30518         }
30519         
30520         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30521         
30522     },
30523     
30524     baseScaleLevel : function()
30525     {
30526         var width, height;
30527         
30528         if(this.isDocument){
30529             
30530             if(this.baseRotate == 6 || this.baseRotate == 8){
30531             
30532                 height = this.thumbEl.getHeight();
30533                 this.baseScale = height / this.imageEl.OriginWidth;
30534
30535                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30536                     width = this.thumbEl.getWidth();
30537                     this.baseScale = width / this.imageEl.OriginHeight;
30538                 }
30539
30540                 return;
30541             }
30542
30543             height = this.thumbEl.getHeight();
30544             this.baseScale = height / this.imageEl.OriginHeight;
30545
30546             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30547                 width = this.thumbEl.getWidth();
30548                 this.baseScale = width / this.imageEl.OriginWidth;
30549             }
30550
30551             return;
30552         }
30553         
30554         if(this.baseRotate == 6 || this.baseRotate == 8){
30555             
30556             width = this.thumbEl.getHeight();
30557             this.baseScale = width / this.imageEl.OriginHeight;
30558             
30559             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30560                 height = this.thumbEl.getWidth();
30561                 this.baseScale = height / this.imageEl.OriginHeight;
30562             }
30563             
30564             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30565                 height = this.thumbEl.getWidth();
30566                 this.baseScale = height / this.imageEl.OriginHeight;
30567                 
30568                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30569                     width = this.thumbEl.getHeight();
30570                     this.baseScale = width / this.imageEl.OriginWidth;
30571                 }
30572             }
30573             
30574             return;
30575         }
30576         
30577         width = this.thumbEl.getWidth();
30578         this.baseScale = width / this.imageEl.OriginWidth;
30579         
30580         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30581             height = this.thumbEl.getHeight();
30582             this.baseScale = height / this.imageEl.OriginHeight;
30583         }
30584         
30585         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30586             
30587             height = this.thumbEl.getHeight();
30588             this.baseScale = height / this.imageEl.OriginHeight;
30589             
30590             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30591                 width = this.thumbEl.getWidth();
30592                 this.baseScale = width / this.imageEl.OriginWidth;
30593             }
30594             
30595         }
30596         
30597         return;
30598     },
30599     
30600     getScaleLevel : function()
30601     {
30602         return this.baseScale * Math.pow(1.1, this.scale);
30603     },
30604     
30605     onTouchStart : function(e)
30606     {
30607         if(!this.canvasLoaded){
30608             this.beforeSelectFile(e);
30609             return;
30610         }
30611         
30612         var touches = e.browserEvent.touches;
30613         
30614         if(!touches){
30615             return;
30616         }
30617         
30618         if(touches.length == 1){
30619             this.onMouseDown(e);
30620             return;
30621         }
30622         
30623         if(touches.length != 2){
30624             return;
30625         }
30626         
30627         var coords = [];
30628         
30629         for(var i = 0, finger; finger = touches[i]; i++){
30630             coords.push(finger.pageX, finger.pageY);
30631         }
30632         
30633         var x = Math.pow(coords[0] - coords[2], 2);
30634         var y = Math.pow(coords[1] - coords[3], 2);
30635         
30636         this.startDistance = Math.sqrt(x + y);
30637         
30638         this.startScale = this.scale;
30639         
30640         this.pinching = true;
30641         this.dragable = false;
30642         
30643     },
30644     
30645     onTouchMove : function(e)
30646     {
30647         if(!this.pinching && !this.dragable){
30648             return;
30649         }
30650         
30651         var touches = e.browserEvent.touches;
30652         
30653         if(!touches){
30654             return;
30655         }
30656         
30657         if(this.dragable){
30658             this.onMouseMove(e);
30659             return;
30660         }
30661         
30662         var coords = [];
30663         
30664         for(var i = 0, finger; finger = touches[i]; i++){
30665             coords.push(finger.pageX, finger.pageY);
30666         }
30667         
30668         var x = Math.pow(coords[0] - coords[2], 2);
30669         var y = Math.pow(coords[1] - coords[3], 2);
30670         
30671         this.endDistance = Math.sqrt(x + y);
30672         
30673         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30674         
30675         if(!this.zoomable()){
30676             this.scale = this.startScale;
30677             return;
30678         }
30679         
30680         this.draw();
30681         
30682     },
30683     
30684     onTouchEnd : function(e)
30685     {
30686         this.pinching = false;
30687         this.dragable = false;
30688         
30689     },
30690     
30691     process : function(file, crop)
30692     {
30693         if(this.loadMask){
30694             this.maskEl.mask(this.loadingText);
30695         }
30696         
30697         this.xhr = new XMLHttpRequest();
30698         
30699         file.xhr = this.xhr;
30700
30701         this.xhr.open(this.method, this.url, true);
30702         
30703         var headers = {
30704             "Accept": "application/json",
30705             "Cache-Control": "no-cache",
30706             "X-Requested-With": "XMLHttpRequest"
30707         };
30708         
30709         for (var headerName in headers) {
30710             var headerValue = headers[headerName];
30711             if (headerValue) {
30712                 this.xhr.setRequestHeader(headerName, headerValue);
30713             }
30714         }
30715         
30716         var _this = this;
30717         
30718         this.xhr.onload = function()
30719         {
30720             _this.xhrOnLoad(_this.xhr);
30721         }
30722         
30723         this.xhr.onerror = function()
30724         {
30725             _this.xhrOnError(_this.xhr);
30726         }
30727         
30728         var formData = new FormData();
30729
30730         formData.append('returnHTML', 'NO');
30731         
30732         if(crop){
30733             formData.append('crop', crop);
30734         }
30735         
30736         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30737             formData.append(this.paramName, file, file.name);
30738         }
30739         
30740         if(typeof(file.filename) != 'undefined'){
30741             formData.append('filename', file.filename);
30742         }
30743         
30744         if(typeof(file.mimetype) != 'undefined'){
30745             formData.append('mimetype', file.mimetype);
30746         }
30747         
30748         if(this.fireEvent('arrange', this, formData) != false){
30749             this.xhr.send(formData);
30750         };
30751     },
30752     
30753     xhrOnLoad : function(xhr)
30754     {
30755         if(this.loadMask){
30756             this.maskEl.unmask();
30757         }
30758         
30759         if (xhr.readyState !== 4) {
30760             this.fireEvent('exception', this, xhr);
30761             return;
30762         }
30763
30764         var response = Roo.decode(xhr.responseText);
30765         
30766         if(!response.success){
30767             this.fireEvent('exception', this, xhr);
30768             return;
30769         }
30770         
30771         var response = Roo.decode(xhr.responseText);
30772         
30773         this.fireEvent('upload', this, response);
30774         
30775     },
30776     
30777     xhrOnError : function()
30778     {
30779         if(this.loadMask){
30780             this.maskEl.unmask();
30781         }
30782         
30783         Roo.log('xhr on error');
30784         
30785         var response = Roo.decode(xhr.responseText);
30786           
30787         Roo.log(response);
30788         
30789     },
30790     
30791     prepare : function(file)
30792     {   
30793         if(this.loadMask){
30794             this.maskEl.mask(this.loadingText);
30795         }
30796         
30797         this.file = false;
30798         this.exif = {};
30799         
30800         if(typeof(file) === 'string'){
30801             this.loadCanvas(file);
30802             return;
30803         }
30804         
30805         if(!file || !this.urlAPI){
30806             return;
30807         }
30808         
30809         this.file = file;
30810         this.cropType = file.type;
30811         
30812         var _this = this;
30813         
30814         if(this.fireEvent('prepare', this, this.file) != false){
30815             
30816             var reader = new FileReader();
30817             
30818             reader.onload = function (e) {
30819                 if (e.target.error) {
30820                     Roo.log(e.target.error);
30821                     return;
30822                 }
30823                 
30824                 var buffer = e.target.result,
30825                     dataView = new DataView(buffer),
30826                     offset = 2,
30827                     maxOffset = dataView.byteLength - 4,
30828                     markerBytes,
30829                     markerLength;
30830                 
30831                 if (dataView.getUint16(0) === 0xffd8) {
30832                     while (offset < maxOffset) {
30833                         markerBytes = dataView.getUint16(offset);
30834                         
30835                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30836                             markerLength = dataView.getUint16(offset + 2) + 2;
30837                             if (offset + markerLength > dataView.byteLength) {
30838                                 Roo.log('Invalid meta data: Invalid segment size.');
30839                                 break;
30840                             }
30841                             
30842                             if(markerBytes == 0xffe1){
30843                                 _this.parseExifData(
30844                                     dataView,
30845                                     offset,
30846                                     markerLength
30847                                 );
30848                             }
30849                             
30850                             offset += markerLength;
30851                             
30852                             continue;
30853                         }
30854                         
30855                         break;
30856                     }
30857                     
30858                 }
30859                 
30860                 var url = _this.urlAPI.createObjectURL(_this.file);
30861                 
30862                 _this.loadCanvas(url);
30863                 
30864                 return;
30865             }
30866             
30867             reader.readAsArrayBuffer(this.file);
30868             
30869         }
30870         
30871     },
30872     
30873     parseExifData : function(dataView, offset, length)
30874     {
30875         var tiffOffset = offset + 10,
30876             littleEndian,
30877             dirOffset;
30878     
30879         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30880             // No Exif data, might be XMP data instead
30881             return;
30882         }
30883         
30884         // Check for the ASCII code for "Exif" (0x45786966):
30885         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30886             // No Exif data, might be XMP data instead
30887             return;
30888         }
30889         if (tiffOffset + 8 > dataView.byteLength) {
30890             Roo.log('Invalid Exif data: Invalid segment size.');
30891             return;
30892         }
30893         // Check for the two null bytes:
30894         if (dataView.getUint16(offset + 8) !== 0x0000) {
30895             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30896             return;
30897         }
30898         // Check the byte alignment:
30899         switch (dataView.getUint16(tiffOffset)) {
30900         case 0x4949:
30901             littleEndian = true;
30902             break;
30903         case 0x4D4D:
30904             littleEndian = false;
30905             break;
30906         default:
30907             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30908             return;
30909         }
30910         // Check for the TIFF tag marker (0x002A):
30911         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30912             Roo.log('Invalid Exif data: Missing TIFF marker.');
30913             return;
30914         }
30915         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30916         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30917         
30918         this.parseExifTags(
30919             dataView,
30920             tiffOffset,
30921             tiffOffset + dirOffset,
30922             littleEndian
30923         );
30924     },
30925     
30926     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30927     {
30928         var tagsNumber,
30929             dirEndOffset,
30930             i;
30931         if (dirOffset + 6 > dataView.byteLength) {
30932             Roo.log('Invalid Exif data: Invalid directory offset.');
30933             return;
30934         }
30935         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30936         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30937         if (dirEndOffset + 4 > dataView.byteLength) {
30938             Roo.log('Invalid Exif data: Invalid directory size.');
30939             return;
30940         }
30941         for (i = 0; i < tagsNumber; i += 1) {
30942             this.parseExifTag(
30943                 dataView,
30944                 tiffOffset,
30945                 dirOffset + 2 + 12 * i, // tag offset
30946                 littleEndian
30947             );
30948         }
30949         // Return the offset to the next directory:
30950         return dataView.getUint32(dirEndOffset, littleEndian);
30951     },
30952     
30953     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30954     {
30955         var tag = dataView.getUint16(offset, littleEndian);
30956         
30957         this.exif[tag] = this.getExifValue(
30958             dataView,
30959             tiffOffset,
30960             offset,
30961             dataView.getUint16(offset + 2, littleEndian), // tag type
30962             dataView.getUint32(offset + 4, littleEndian), // tag length
30963             littleEndian
30964         );
30965     },
30966     
30967     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30968     {
30969         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30970             tagSize,
30971             dataOffset,
30972             values,
30973             i,
30974             str,
30975             c;
30976     
30977         if (!tagType) {
30978             Roo.log('Invalid Exif data: Invalid tag type.');
30979             return;
30980         }
30981         
30982         tagSize = tagType.size * length;
30983         // Determine if the value is contained in the dataOffset bytes,
30984         // or if the value at the dataOffset is a pointer to the actual data:
30985         dataOffset = tagSize > 4 ?
30986                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30987         if (dataOffset + tagSize > dataView.byteLength) {
30988             Roo.log('Invalid Exif data: Invalid data offset.');
30989             return;
30990         }
30991         if (length === 1) {
30992             return tagType.getValue(dataView, dataOffset, littleEndian);
30993         }
30994         values = [];
30995         for (i = 0; i < length; i += 1) {
30996             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30997         }
30998         
30999         if (tagType.ascii) {
31000             str = '';
31001             // Concatenate the chars:
31002             for (i = 0; i < values.length; i += 1) {
31003                 c = values[i];
31004                 // Ignore the terminating NULL byte(s):
31005                 if (c === '\u0000') {
31006                     break;
31007                 }
31008                 str += c;
31009             }
31010             return str;
31011         }
31012         return values;
31013     }
31014     
31015 });
31016
31017 Roo.apply(Roo.bootstrap.UploadCropbox, {
31018     tags : {
31019         'Orientation': 0x0112
31020     },
31021     
31022     Orientation: {
31023             1: 0, //'top-left',
31024 //            2: 'top-right',
31025             3: 180, //'bottom-right',
31026 //            4: 'bottom-left',
31027 //            5: 'left-top',
31028             6: 90, //'right-top',
31029 //            7: 'right-bottom',
31030             8: 270 //'left-bottom'
31031     },
31032     
31033     exifTagTypes : {
31034         // byte, 8-bit unsigned int:
31035         1: {
31036             getValue: function (dataView, dataOffset) {
31037                 return dataView.getUint8(dataOffset);
31038             },
31039             size: 1
31040         },
31041         // ascii, 8-bit byte:
31042         2: {
31043             getValue: function (dataView, dataOffset) {
31044                 return String.fromCharCode(dataView.getUint8(dataOffset));
31045             },
31046             size: 1,
31047             ascii: true
31048         },
31049         // short, 16 bit int:
31050         3: {
31051             getValue: function (dataView, dataOffset, littleEndian) {
31052                 return dataView.getUint16(dataOffset, littleEndian);
31053             },
31054             size: 2
31055         },
31056         // long, 32 bit int:
31057         4: {
31058             getValue: function (dataView, dataOffset, littleEndian) {
31059                 return dataView.getUint32(dataOffset, littleEndian);
31060             },
31061             size: 4
31062         },
31063         // rational = two long values, first is numerator, second is denominator:
31064         5: {
31065             getValue: function (dataView, dataOffset, littleEndian) {
31066                 return dataView.getUint32(dataOffset, littleEndian) /
31067                     dataView.getUint32(dataOffset + 4, littleEndian);
31068             },
31069             size: 8
31070         },
31071         // slong, 32 bit signed int:
31072         9: {
31073             getValue: function (dataView, dataOffset, littleEndian) {
31074                 return dataView.getInt32(dataOffset, littleEndian);
31075             },
31076             size: 4
31077         },
31078         // srational, two slongs, first is numerator, second is denominator:
31079         10: {
31080             getValue: function (dataView, dataOffset, littleEndian) {
31081                 return dataView.getInt32(dataOffset, littleEndian) /
31082                     dataView.getInt32(dataOffset + 4, littleEndian);
31083             },
31084             size: 8
31085         }
31086     },
31087     
31088     footer : {
31089         STANDARD : [
31090             {
31091                 tag : 'div',
31092                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31093                 action : 'rotate-left',
31094                 cn : [
31095                     {
31096                         tag : 'button',
31097                         cls : 'btn btn-default',
31098                         html : '<i class="fa fa-undo"></i>'
31099                     }
31100                 ]
31101             },
31102             {
31103                 tag : 'div',
31104                 cls : 'btn-group roo-upload-cropbox-picture',
31105                 action : 'picture',
31106                 cn : [
31107                     {
31108                         tag : 'button',
31109                         cls : 'btn btn-default',
31110                         html : '<i class="fa fa-picture-o"></i>'
31111                     }
31112                 ]
31113             },
31114             {
31115                 tag : 'div',
31116                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31117                 action : 'rotate-right',
31118                 cn : [
31119                     {
31120                         tag : 'button',
31121                         cls : 'btn btn-default',
31122                         html : '<i class="fa fa-repeat"></i>'
31123                     }
31124                 ]
31125             }
31126         ],
31127         DOCUMENT : [
31128             {
31129                 tag : 'div',
31130                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31131                 action : 'rotate-left',
31132                 cn : [
31133                     {
31134                         tag : 'button',
31135                         cls : 'btn btn-default',
31136                         html : '<i class="fa fa-undo"></i>'
31137                     }
31138                 ]
31139             },
31140             {
31141                 tag : 'div',
31142                 cls : 'btn-group roo-upload-cropbox-download',
31143                 action : 'download',
31144                 cn : [
31145                     {
31146                         tag : 'button',
31147                         cls : 'btn btn-default',
31148                         html : '<i class="fa fa-download"></i>'
31149                     }
31150                 ]
31151             },
31152             {
31153                 tag : 'div',
31154                 cls : 'btn-group roo-upload-cropbox-crop',
31155                 action : 'crop',
31156                 cn : [
31157                     {
31158                         tag : 'button',
31159                         cls : 'btn btn-default',
31160                         html : '<i class="fa fa-crop"></i>'
31161                     }
31162                 ]
31163             },
31164             {
31165                 tag : 'div',
31166                 cls : 'btn-group roo-upload-cropbox-trash',
31167                 action : 'trash',
31168                 cn : [
31169                     {
31170                         tag : 'button',
31171                         cls : 'btn btn-default',
31172                         html : '<i class="fa fa-trash"></i>'
31173                     }
31174                 ]
31175             },
31176             {
31177                 tag : 'div',
31178                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31179                 action : 'rotate-right',
31180                 cn : [
31181                     {
31182                         tag : 'button',
31183                         cls : 'btn btn-default',
31184                         html : '<i class="fa fa-repeat"></i>'
31185                     }
31186                 ]
31187             }
31188         ],
31189         ROTATOR : [
31190             {
31191                 tag : 'div',
31192                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31193                 action : 'rotate-left',
31194                 cn : [
31195                     {
31196                         tag : 'button',
31197                         cls : 'btn btn-default',
31198                         html : '<i class="fa fa-undo"></i>'
31199                     }
31200                 ]
31201             },
31202             {
31203                 tag : 'div',
31204                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31205                 action : 'rotate-right',
31206                 cn : [
31207                     {
31208                         tag : 'button',
31209                         cls : 'btn btn-default',
31210                         html : '<i class="fa fa-repeat"></i>'
31211                     }
31212                 ]
31213             }
31214         ]
31215     }
31216 });
31217
31218 /*
31219 * Licence: LGPL
31220 */
31221
31222 /**
31223  * @class Roo.bootstrap.DocumentManager
31224  * @extends Roo.bootstrap.Component
31225  * Bootstrap DocumentManager class
31226  * @cfg {String} paramName default 'imageUpload'
31227  * @cfg {String} toolTipName default 'filename'
31228  * @cfg {String} method default POST
31229  * @cfg {String} url action url
31230  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31231  * @cfg {Boolean} multiple multiple upload default true
31232  * @cfg {Number} thumbSize default 300
31233  * @cfg {String} fieldLabel
31234  * @cfg {Number} labelWidth default 4
31235  * @cfg {String} labelAlign (left|top) default left
31236  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31237 * @cfg {Number} labellg set the width of label (1-12)
31238  * @cfg {Number} labelmd set the width of label (1-12)
31239  * @cfg {Number} labelsm set the width of label (1-12)
31240  * @cfg {Number} labelxs set the width of label (1-12)
31241  * 
31242  * @constructor
31243  * Create a new DocumentManager
31244  * @param {Object} config The config object
31245  */
31246
31247 Roo.bootstrap.DocumentManager = function(config){
31248     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31249     
31250     this.files = [];
31251     this.delegates = [];
31252     
31253     this.addEvents({
31254         /**
31255          * @event initial
31256          * Fire when initial the DocumentManager
31257          * @param {Roo.bootstrap.DocumentManager} this
31258          */
31259         "initial" : true,
31260         /**
31261          * @event inspect
31262          * inspect selected file
31263          * @param {Roo.bootstrap.DocumentManager} this
31264          * @param {File} file
31265          */
31266         "inspect" : true,
31267         /**
31268          * @event exception
31269          * Fire when xhr load exception
31270          * @param {Roo.bootstrap.DocumentManager} this
31271          * @param {XMLHttpRequest} xhr
31272          */
31273         "exception" : true,
31274         /**
31275          * @event afterupload
31276          * Fire when xhr load exception
31277          * @param {Roo.bootstrap.DocumentManager} this
31278          * @param {XMLHttpRequest} xhr
31279          */
31280         "afterupload" : true,
31281         /**
31282          * @event prepare
31283          * prepare the form data
31284          * @param {Roo.bootstrap.DocumentManager} this
31285          * @param {Object} formData
31286          */
31287         "prepare" : true,
31288         /**
31289          * @event remove
31290          * Fire when remove the file
31291          * @param {Roo.bootstrap.DocumentManager} this
31292          * @param {Object} file
31293          */
31294         "remove" : true,
31295         /**
31296          * @event refresh
31297          * Fire after refresh the file
31298          * @param {Roo.bootstrap.DocumentManager} this
31299          */
31300         "refresh" : true,
31301         /**
31302          * @event click
31303          * Fire after click the image
31304          * @param {Roo.bootstrap.DocumentManager} this
31305          * @param {Object} file
31306          */
31307         "click" : true,
31308         /**
31309          * @event edit
31310          * Fire when upload a image and editable set to true
31311          * @param {Roo.bootstrap.DocumentManager} this
31312          * @param {Object} file
31313          */
31314         "edit" : true,
31315         /**
31316          * @event beforeselectfile
31317          * Fire before select file
31318          * @param {Roo.bootstrap.DocumentManager} this
31319          */
31320         "beforeselectfile" : true,
31321         /**
31322          * @event process
31323          * Fire before process file
31324          * @param {Roo.bootstrap.DocumentManager} this
31325          * @param {Object} file
31326          */
31327         "process" : true,
31328         /**
31329          * @event previewrendered
31330          * Fire when preview rendered
31331          * @param {Roo.bootstrap.DocumentManager} this
31332          * @param {Object} file
31333          */
31334         "previewrendered" : true,
31335         /**
31336          */
31337         "previewResize" : true
31338         
31339     });
31340 };
31341
31342 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31343     
31344     boxes : 0,
31345     inputName : '',
31346     thumbSize : 300,
31347     multiple : true,
31348     files : false,
31349     method : 'POST',
31350     url : '',
31351     paramName : 'imageUpload',
31352     toolTipName : 'filename',
31353     fieldLabel : '',
31354     labelWidth : 4,
31355     labelAlign : 'left',
31356     editable : true,
31357     delegates : false,
31358     xhr : false, 
31359     
31360     labellg : 0,
31361     labelmd : 0,
31362     labelsm : 0,
31363     labelxs : 0,
31364     
31365     getAutoCreate : function()
31366     {   
31367         var managerWidget = {
31368             tag : 'div',
31369             cls : 'roo-document-manager',
31370             cn : [
31371                 {
31372                     tag : 'input',
31373                     cls : 'roo-document-manager-selector',
31374                     type : 'file'
31375                 },
31376                 {
31377                     tag : 'div',
31378                     cls : 'roo-document-manager-uploader',
31379                     cn : [
31380                         {
31381                             tag : 'div',
31382                             cls : 'roo-document-manager-upload-btn',
31383                             html : '<i class="fa fa-plus"></i>'
31384                         }
31385                     ]
31386                     
31387                 }
31388             ]
31389         };
31390         
31391         var content = [
31392             {
31393                 tag : 'div',
31394                 cls : 'column col-md-12',
31395                 cn : managerWidget
31396             }
31397         ];
31398         
31399         if(this.fieldLabel.length){
31400             
31401             content = [
31402                 {
31403                     tag : 'div',
31404                     cls : 'column col-md-12',
31405                     html : this.fieldLabel
31406                 },
31407                 {
31408                     tag : 'div',
31409                     cls : 'column col-md-12',
31410                     cn : managerWidget
31411                 }
31412             ];
31413
31414             if(this.labelAlign == 'left'){
31415                 content = [
31416                     {
31417                         tag : 'div',
31418                         cls : 'column',
31419                         html : this.fieldLabel
31420                     },
31421                     {
31422                         tag : 'div',
31423                         cls : 'column',
31424                         cn : managerWidget
31425                     }
31426                 ];
31427                 
31428                 if(this.labelWidth > 12){
31429                     content[0].style = "width: " + this.labelWidth + 'px';
31430                 }
31431
31432                 if(this.labelWidth < 13 && this.labelmd == 0){
31433                     this.labelmd = this.labelWidth;
31434                 }
31435
31436                 if(this.labellg > 0){
31437                     content[0].cls += ' col-lg-' + this.labellg;
31438                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31439                 }
31440
31441                 if(this.labelmd > 0){
31442                     content[0].cls += ' col-md-' + this.labelmd;
31443                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31444                 }
31445
31446                 if(this.labelsm > 0){
31447                     content[0].cls += ' col-sm-' + this.labelsm;
31448                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31449                 }
31450
31451                 if(this.labelxs > 0){
31452                     content[0].cls += ' col-xs-' + this.labelxs;
31453                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31454                 }
31455                 
31456             }
31457         }
31458         
31459         var cfg = {
31460             tag : 'div',
31461             cls : 'row clearfix',
31462             cn : content
31463         };
31464         
31465         return cfg;
31466         
31467     },
31468     
31469     initEvents : function()
31470     {
31471         this.managerEl = this.el.select('.roo-document-manager', true).first();
31472         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31473         
31474         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31475         this.selectorEl.hide();
31476         
31477         if(this.multiple){
31478             this.selectorEl.attr('multiple', 'multiple');
31479         }
31480         
31481         this.selectorEl.on('change', this.onFileSelected, this);
31482         
31483         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31484         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31485         
31486         this.uploader.on('click', this.onUploaderClick, this);
31487         
31488         this.renderProgressDialog();
31489         
31490         var _this = this;
31491         
31492         window.addEventListener("resize", function() { _this.refresh(); } );
31493         
31494         this.fireEvent('initial', this);
31495     },
31496     
31497     renderProgressDialog : function()
31498     {
31499         var _this = this;
31500         
31501         this.progressDialog = new Roo.bootstrap.Modal({
31502             cls : 'roo-document-manager-progress-dialog',
31503             allow_close : false,
31504             animate : false,
31505             title : '',
31506             buttons : [
31507                 {
31508                     name  :'cancel',
31509                     weight : 'danger',
31510                     html : 'Cancel'
31511                 }
31512             ], 
31513             listeners : { 
31514                 btnclick : function() {
31515                     _this.uploadCancel();
31516                     this.hide();
31517                 }
31518             }
31519         });
31520          
31521         this.progressDialog.render(Roo.get(document.body));
31522          
31523         this.progress = new Roo.bootstrap.Progress({
31524             cls : 'roo-document-manager-progress',
31525             active : true,
31526             striped : true
31527         });
31528         
31529         this.progress.render(this.progressDialog.getChildContainer());
31530         
31531         this.progressBar = new Roo.bootstrap.ProgressBar({
31532             cls : 'roo-document-manager-progress-bar',
31533             aria_valuenow : 0,
31534             aria_valuemin : 0,
31535             aria_valuemax : 12,
31536             panel : 'success'
31537         });
31538         
31539         this.progressBar.render(this.progress.getChildContainer());
31540     },
31541     
31542     onUploaderClick : function(e)
31543     {
31544         e.preventDefault();
31545      
31546         if(this.fireEvent('beforeselectfile', this) != false){
31547             this.selectorEl.dom.click();
31548         }
31549         
31550     },
31551     
31552     onFileSelected : function(e)
31553     {
31554         e.preventDefault();
31555         
31556         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31557             return;
31558         }
31559         
31560         Roo.each(this.selectorEl.dom.files, function(file){
31561             if(this.fireEvent('inspect', this, file) != false){
31562                 this.files.push(file);
31563             }
31564         }, this);
31565         
31566         this.queue();
31567         
31568     },
31569     
31570     queue : function()
31571     {
31572         this.selectorEl.dom.value = '';
31573         
31574         if(!this.files || !this.files.length){
31575             return;
31576         }
31577         
31578         if(this.boxes > 0 && this.files.length > this.boxes){
31579             this.files = this.files.slice(0, this.boxes);
31580         }
31581         
31582         this.uploader.show();
31583         
31584         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31585             this.uploader.hide();
31586         }
31587         
31588         var _this = this;
31589         
31590         var files = [];
31591         
31592         var docs = [];
31593         
31594         Roo.each(this.files, function(file){
31595             
31596             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31597                 var f = this.renderPreview(file);
31598                 files.push(f);
31599                 return;
31600             }
31601             
31602             if(file.type.indexOf('image') != -1){
31603                 this.delegates.push(
31604                     (function(){
31605                         _this.process(file);
31606                     }).createDelegate(this)
31607                 );
31608         
31609                 return;
31610             }
31611             
31612             docs.push(
31613                 (function(){
31614                     _this.process(file);
31615                 }).createDelegate(this)
31616             );
31617             
31618         }, this);
31619         
31620         this.files = files;
31621         
31622         this.delegates = this.delegates.concat(docs);
31623         
31624         if(!this.delegates.length){
31625             this.refresh();
31626             return;
31627         }
31628         
31629         this.progressBar.aria_valuemax = this.delegates.length;
31630         
31631         this.arrange();
31632         
31633         return;
31634     },
31635     
31636     arrange : function()
31637     {
31638         if(!this.delegates.length){
31639             this.progressDialog.hide();
31640             this.refresh();
31641             return;
31642         }
31643         
31644         var delegate = this.delegates.shift();
31645         
31646         this.progressDialog.show();
31647         
31648         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31649         
31650         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31651         
31652         delegate();
31653     },
31654     
31655     refresh : function()
31656     {
31657         this.uploader.show();
31658         
31659         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31660             this.uploader.hide();
31661         }
31662         
31663         Roo.isTouch ? this.closable(false) : this.closable(true);
31664         
31665         this.fireEvent('refresh', this);
31666     },
31667     
31668     onRemove : function(e, el, o)
31669     {
31670         e.preventDefault();
31671         
31672         this.fireEvent('remove', this, o);
31673         
31674     },
31675     
31676     remove : function(o)
31677     {
31678         var files = [];
31679         
31680         Roo.each(this.files, function(file){
31681             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31682                 files.push(file);
31683                 return;
31684             }
31685
31686             o.target.remove();
31687
31688         }, this);
31689         
31690         this.files = files;
31691         
31692         this.refresh();
31693     },
31694     
31695     clear : function()
31696     {
31697         Roo.each(this.files, function(file){
31698             if(!file.target){
31699                 return;
31700             }
31701             
31702             file.target.remove();
31703
31704         }, this);
31705         
31706         this.files = [];
31707         
31708         this.refresh();
31709     },
31710     
31711     onClick : function(e, el, o)
31712     {
31713         e.preventDefault();
31714         
31715         this.fireEvent('click', this, o);
31716         
31717     },
31718     
31719     closable : function(closable)
31720     {
31721         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31722             
31723             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31724             
31725             if(closable){
31726                 el.show();
31727                 return;
31728             }
31729             
31730             el.hide();
31731             
31732         }, this);
31733     },
31734     
31735     xhrOnLoad : function(xhr)
31736     {
31737         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31738             el.remove();
31739         }, this);
31740         
31741         if (xhr.readyState !== 4) {
31742             this.arrange();
31743             this.fireEvent('exception', this, xhr);
31744             return;
31745         }
31746
31747         var response = Roo.decode(xhr.responseText);
31748         
31749         if(!response.success){
31750             this.arrange();
31751             this.fireEvent('exception', this, xhr);
31752             return;
31753         }
31754         
31755         var file = this.renderPreview(response.data);
31756         
31757         this.files.push(file);
31758         
31759         this.arrange();
31760         
31761         this.fireEvent('afterupload', this, xhr);
31762         
31763     },
31764     
31765     xhrOnError : function(xhr)
31766     {
31767         Roo.log('xhr on error');
31768         
31769         var response = Roo.decode(xhr.responseText);
31770           
31771         Roo.log(response);
31772         
31773         this.arrange();
31774     },
31775     
31776     process : function(file)
31777     {
31778         if(this.fireEvent('process', this, file) !== false){
31779             if(this.editable && file.type.indexOf('image') != -1){
31780                 this.fireEvent('edit', this, file);
31781                 return;
31782             }
31783
31784             this.uploadStart(file, false);
31785
31786             return;
31787         }
31788         
31789     },
31790     
31791     uploadStart : function(file, crop)
31792     {
31793         this.xhr = new XMLHttpRequest();
31794         
31795         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31796             this.arrange();
31797             return;
31798         }
31799         
31800         file.xhr = this.xhr;
31801             
31802         this.managerEl.createChild({
31803             tag : 'div',
31804             cls : 'roo-document-manager-loading',
31805             cn : [
31806                 {
31807                     tag : 'div',
31808                     tooltip : file.name,
31809                     cls : 'roo-document-manager-thumb',
31810                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31811                 }
31812             ]
31813
31814         });
31815
31816         this.xhr.open(this.method, this.url, true);
31817         
31818         var headers = {
31819             "Accept": "application/json",
31820             "Cache-Control": "no-cache",
31821             "X-Requested-With": "XMLHttpRequest"
31822         };
31823         
31824         for (var headerName in headers) {
31825             var headerValue = headers[headerName];
31826             if (headerValue) {
31827                 this.xhr.setRequestHeader(headerName, headerValue);
31828             }
31829         }
31830         
31831         var _this = this;
31832         
31833         this.xhr.onload = function()
31834         {
31835             _this.xhrOnLoad(_this.xhr);
31836         }
31837         
31838         this.xhr.onerror = function()
31839         {
31840             _this.xhrOnError(_this.xhr);
31841         }
31842         
31843         var formData = new FormData();
31844
31845         formData.append('returnHTML', 'NO');
31846         
31847         if(crop){
31848             formData.append('crop', crop);
31849         }
31850         
31851         formData.append(this.paramName, file, file.name);
31852         
31853         var options = {
31854             file : file, 
31855             manually : false
31856         };
31857         
31858         if(this.fireEvent('prepare', this, formData, options) != false){
31859             
31860             if(options.manually){
31861                 return;
31862             }
31863             
31864             this.xhr.send(formData);
31865             return;
31866         };
31867         
31868         this.uploadCancel();
31869     },
31870     
31871     uploadCancel : function()
31872     {
31873         if (this.xhr) {
31874             this.xhr.abort();
31875         }
31876         
31877         this.delegates = [];
31878         
31879         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31880             el.remove();
31881         }, this);
31882         
31883         this.arrange();
31884     },
31885     
31886     renderPreview : function(file)
31887     {
31888         if(typeof(file.target) != 'undefined' && file.target){
31889             return file;
31890         }
31891         
31892         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31893         
31894         var previewEl = this.managerEl.createChild({
31895             tag : 'div',
31896             cls : 'roo-document-manager-preview',
31897             cn : [
31898                 {
31899                     tag : 'div',
31900                     tooltip : file[this.toolTipName],
31901                     cls : 'roo-document-manager-thumb',
31902                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31903                 },
31904                 {
31905                     tag : 'button',
31906                     cls : 'close',
31907                     html : '<i class="fa fa-times-circle"></i>'
31908                 }
31909             ]
31910         });
31911
31912         var close = previewEl.select('button.close', true).first();
31913
31914         close.on('click', this.onRemove, this, file);
31915
31916         file.target = previewEl;
31917
31918         var image = previewEl.select('img', true).first();
31919         
31920         var _this = this;
31921         
31922         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31923         
31924         image.on('click', this.onClick, this, file);
31925         
31926         this.fireEvent('previewrendered', this, file);
31927         
31928         return file;
31929         
31930     },
31931     
31932     onPreviewLoad : function(file, image)
31933     {
31934         if(typeof(file.target) == 'undefined' || !file.target){
31935             return;
31936         }
31937         
31938         var width = image.dom.naturalWidth || image.dom.width;
31939         var height = image.dom.naturalHeight || image.dom.height;
31940         
31941         if(!this.previewResize) {
31942             return;
31943         }
31944         
31945         if(width > height){
31946             file.target.addClass('wide');
31947             return;
31948         }
31949         
31950         file.target.addClass('tall');
31951         return;
31952         
31953     },
31954     
31955     uploadFromSource : function(file, crop)
31956     {
31957         this.xhr = new XMLHttpRequest();
31958         
31959         this.managerEl.createChild({
31960             tag : 'div',
31961             cls : 'roo-document-manager-loading',
31962             cn : [
31963                 {
31964                     tag : 'div',
31965                     tooltip : file.name,
31966                     cls : 'roo-document-manager-thumb',
31967                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31968                 }
31969             ]
31970
31971         });
31972
31973         this.xhr.open(this.method, this.url, true);
31974         
31975         var headers = {
31976             "Accept": "application/json",
31977             "Cache-Control": "no-cache",
31978             "X-Requested-With": "XMLHttpRequest"
31979         };
31980         
31981         for (var headerName in headers) {
31982             var headerValue = headers[headerName];
31983             if (headerValue) {
31984                 this.xhr.setRequestHeader(headerName, headerValue);
31985             }
31986         }
31987         
31988         var _this = this;
31989         
31990         this.xhr.onload = function()
31991         {
31992             _this.xhrOnLoad(_this.xhr);
31993         }
31994         
31995         this.xhr.onerror = function()
31996         {
31997             _this.xhrOnError(_this.xhr);
31998         }
31999         
32000         var formData = new FormData();
32001
32002         formData.append('returnHTML', 'NO');
32003         
32004         formData.append('crop', crop);
32005         
32006         if(typeof(file.filename) != 'undefined'){
32007             formData.append('filename', file.filename);
32008         }
32009         
32010         if(typeof(file.mimetype) != 'undefined'){
32011             formData.append('mimetype', file.mimetype);
32012         }
32013         
32014         Roo.log(formData);
32015         
32016         if(this.fireEvent('prepare', this, formData) != false){
32017             this.xhr.send(formData);
32018         };
32019     }
32020 });
32021
32022 /*
32023 * Licence: LGPL
32024 */
32025
32026 /**
32027  * @class Roo.bootstrap.DocumentViewer
32028  * @extends Roo.bootstrap.Component
32029  * Bootstrap DocumentViewer class
32030  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32031  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32032  * 
32033  * @constructor
32034  * Create a new DocumentViewer
32035  * @param {Object} config The config object
32036  */
32037
32038 Roo.bootstrap.DocumentViewer = function(config){
32039     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32040     
32041     this.addEvents({
32042         /**
32043          * @event initial
32044          * Fire after initEvent
32045          * @param {Roo.bootstrap.DocumentViewer} this
32046          */
32047         "initial" : true,
32048         /**
32049          * @event click
32050          * Fire after click
32051          * @param {Roo.bootstrap.DocumentViewer} this
32052          */
32053         "click" : true,
32054         /**
32055          * @event download
32056          * Fire after download button
32057          * @param {Roo.bootstrap.DocumentViewer} this
32058          */
32059         "download" : true,
32060         /**
32061          * @event trash
32062          * Fire after trash button
32063          * @param {Roo.bootstrap.DocumentViewer} this
32064          */
32065         "trash" : true
32066         
32067     });
32068 };
32069
32070 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32071     
32072     showDownload : true,
32073     
32074     showTrash : true,
32075     
32076     getAutoCreate : function()
32077     {
32078         var cfg = {
32079             tag : 'div',
32080             cls : 'roo-document-viewer',
32081             cn : [
32082                 {
32083                     tag : 'div',
32084                     cls : 'roo-document-viewer-body',
32085                     cn : [
32086                         {
32087                             tag : 'div',
32088                             cls : 'roo-document-viewer-thumb',
32089                             cn : [
32090                                 {
32091                                     tag : 'img',
32092                                     cls : 'roo-document-viewer-image'
32093                                 }
32094                             ]
32095                         }
32096                     ]
32097                 },
32098                 {
32099                     tag : 'div',
32100                     cls : 'roo-document-viewer-footer',
32101                     cn : {
32102                         tag : 'div',
32103                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32104                         cn : [
32105                             {
32106                                 tag : 'div',
32107                                 cls : 'btn-group roo-document-viewer-download',
32108                                 cn : [
32109                                     {
32110                                         tag : 'button',
32111                                         cls : 'btn btn-default',
32112                                         html : '<i class="fa fa-download"></i>'
32113                                     }
32114                                 ]
32115                             },
32116                             {
32117                                 tag : 'div',
32118                                 cls : 'btn-group roo-document-viewer-trash',
32119                                 cn : [
32120                                     {
32121                                         tag : 'button',
32122                                         cls : 'btn btn-default',
32123                                         html : '<i class="fa fa-trash"></i>'
32124                                     }
32125                                 ]
32126                             }
32127                         ]
32128                     }
32129                 }
32130             ]
32131         };
32132         
32133         return cfg;
32134     },
32135     
32136     initEvents : function()
32137     {
32138         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32139         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32140         
32141         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32142         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32143         
32144         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32145         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32146         
32147         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32148         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32149         
32150         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32151         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32152         
32153         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32154         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32155         
32156         this.bodyEl.on('click', this.onClick, this);
32157         this.downloadBtn.on('click', this.onDownload, this);
32158         this.trashBtn.on('click', this.onTrash, this);
32159         
32160         this.downloadBtn.hide();
32161         this.trashBtn.hide();
32162         
32163         if(this.showDownload){
32164             this.downloadBtn.show();
32165         }
32166         
32167         if(this.showTrash){
32168             this.trashBtn.show();
32169         }
32170         
32171         if(!this.showDownload && !this.showTrash) {
32172             this.footerEl.hide();
32173         }
32174         
32175     },
32176     
32177     initial : function()
32178     {
32179         this.fireEvent('initial', this);
32180         
32181     },
32182     
32183     onClick : function(e)
32184     {
32185         e.preventDefault();
32186         
32187         this.fireEvent('click', this);
32188     },
32189     
32190     onDownload : function(e)
32191     {
32192         e.preventDefault();
32193         
32194         this.fireEvent('download', this);
32195     },
32196     
32197     onTrash : function(e)
32198     {
32199         e.preventDefault();
32200         
32201         this.fireEvent('trash', this);
32202     }
32203     
32204 });
32205 /*
32206  * - LGPL
32207  *
32208  * nav progress bar
32209  * 
32210  */
32211
32212 /**
32213  * @class Roo.bootstrap.NavProgressBar
32214  * @extends Roo.bootstrap.Component
32215  * Bootstrap NavProgressBar class
32216  * 
32217  * @constructor
32218  * Create a new nav progress bar
32219  * @param {Object} config The config object
32220  */
32221
32222 Roo.bootstrap.NavProgressBar = function(config){
32223     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32224
32225     this.bullets = this.bullets || [];
32226    
32227 //    Roo.bootstrap.NavProgressBar.register(this);
32228      this.addEvents({
32229         /**
32230              * @event changed
32231              * Fires when the active item changes
32232              * @param {Roo.bootstrap.NavProgressBar} this
32233              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32234              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32235          */
32236         'changed': true
32237      });
32238     
32239 };
32240
32241 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32242     
32243     bullets : [],
32244     barItems : [],
32245     
32246     getAutoCreate : function()
32247     {
32248         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32249         
32250         cfg = {
32251             tag : 'div',
32252             cls : 'roo-navigation-bar-group',
32253             cn : [
32254                 {
32255                     tag : 'div',
32256                     cls : 'roo-navigation-top-bar'
32257                 },
32258                 {
32259                     tag : 'div',
32260                     cls : 'roo-navigation-bullets-bar',
32261                     cn : [
32262                         {
32263                             tag : 'ul',
32264                             cls : 'roo-navigation-bar'
32265                         }
32266                     ]
32267                 },
32268                 
32269                 {
32270                     tag : 'div',
32271                     cls : 'roo-navigation-bottom-bar'
32272                 }
32273             ]
32274             
32275         };
32276         
32277         return cfg;
32278         
32279     },
32280     
32281     initEvents: function() 
32282     {
32283         
32284     },
32285     
32286     onRender : function(ct, position) 
32287     {
32288         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32289         
32290         if(this.bullets.length){
32291             Roo.each(this.bullets, function(b){
32292                this.addItem(b);
32293             }, this);
32294         }
32295         
32296         this.format();
32297         
32298     },
32299     
32300     addItem : function(cfg)
32301     {
32302         var item = new Roo.bootstrap.NavProgressItem(cfg);
32303         
32304         item.parentId = this.id;
32305         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32306         
32307         if(cfg.html){
32308             var top = new Roo.bootstrap.Element({
32309                 tag : 'div',
32310                 cls : 'roo-navigation-bar-text'
32311             });
32312             
32313             var bottom = new Roo.bootstrap.Element({
32314                 tag : 'div',
32315                 cls : 'roo-navigation-bar-text'
32316             });
32317             
32318             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32319             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32320             
32321             var topText = new Roo.bootstrap.Element({
32322                 tag : 'span',
32323                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32324             });
32325             
32326             var bottomText = new Roo.bootstrap.Element({
32327                 tag : 'span',
32328                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32329             });
32330             
32331             topText.onRender(top.el, null);
32332             bottomText.onRender(bottom.el, null);
32333             
32334             item.topEl = top;
32335             item.bottomEl = bottom;
32336         }
32337         
32338         this.barItems.push(item);
32339         
32340         return item;
32341     },
32342     
32343     getActive : function()
32344     {
32345         var active = false;
32346         
32347         Roo.each(this.barItems, function(v){
32348             
32349             if (!v.isActive()) {
32350                 return;
32351             }
32352             
32353             active = v;
32354             return false;
32355             
32356         });
32357         
32358         return active;
32359     },
32360     
32361     setActiveItem : function(item)
32362     {
32363         var prev = false;
32364         
32365         Roo.each(this.barItems, function(v){
32366             if (v.rid == item.rid) {
32367                 return ;
32368             }
32369             
32370             if (v.isActive()) {
32371                 v.setActive(false);
32372                 prev = v;
32373             }
32374         });
32375
32376         item.setActive(true);
32377         
32378         this.fireEvent('changed', this, item, prev);
32379     },
32380     
32381     getBarItem: function(rid)
32382     {
32383         var ret = false;
32384         
32385         Roo.each(this.barItems, function(e) {
32386             if (e.rid != rid) {
32387                 return;
32388             }
32389             
32390             ret =  e;
32391             return false;
32392         });
32393         
32394         return ret;
32395     },
32396     
32397     indexOfItem : function(item)
32398     {
32399         var index = false;
32400         
32401         Roo.each(this.barItems, function(v, i){
32402             
32403             if (v.rid != item.rid) {
32404                 return;
32405             }
32406             
32407             index = i;
32408             return false
32409         });
32410         
32411         return index;
32412     },
32413     
32414     setActiveNext : function()
32415     {
32416         var i = this.indexOfItem(this.getActive());
32417         
32418         if (i > this.barItems.length) {
32419             return;
32420         }
32421         
32422         this.setActiveItem(this.barItems[i+1]);
32423     },
32424     
32425     setActivePrev : function()
32426     {
32427         var i = this.indexOfItem(this.getActive());
32428         
32429         if (i  < 1) {
32430             return;
32431         }
32432         
32433         this.setActiveItem(this.barItems[i-1]);
32434     },
32435     
32436     format : function()
32437     {
32438         if(!this.barItems.length){
32439             return;
32440         }
32441      
32442         var width = 100 / this.barItems.length;
32443         
32444         Roo.each(this.barItems, function(i){
32445             i.el.setStyle('width', width + '%');
32446             i.topEl.el.setStyle('width', width + '%');
32447             i.bottomEl.el.setStyle('width', width + '%');
32448         }, this);
32449         
32450     }
32451     
32452 });
32453 /*
32454  * - LGPL
32455  *
32456  * Nav Progress Item
32457  * 
32458  */
32459
32460 /**
32461  * @class Roo.bootstrap.NavProgressItem
32462  * @extends Roo.bootstrap.Component
32463  * Bootstrap NavProgressItem class
32464  * @cfg {String} rid the reference id
32465  * @cfg {Boolean} active (true|false) Is item active default false
32466  * @cfg {Boolean} disabled (true|false) Is item active default false
32467  * @cfg {String} html
32468  * @cfg {String} position (top|bottom) text position default bottom
32469  * @cfg {String} icon show icon instead of number
32470  * 
32471  * @constructor
32472  * Create a new NavProgressItem
32473  * @param {Object} config The config object
32474  */
32475 Roo.bootstrap.NavProgressItem = function(config){
32476     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32477     this.addEvents({
32478         // raw events
32479         /**
32480          * @event click
32481          * The raw click event for the entire grid.
32482          * @param {Roo.bootstrap.NavProgressItem} this
32483          * @param {Roo.EventObject} e
32484          */
32485         "click" : true
32486     });
32487    
32488 };
32489
32490 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32491     
32492     rid : '',
32493     active : false,
32494     disabled : false,
32495     html : '',
32496     position : 'bottom',
32497     icon : false,
32498     
32499     getAutoCreate : function()
32500     {
32501         var iconCls = 'roo-navigation-bar-item-icon';
32502         
32503         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32504         
32505         var cfg = {
32506             tag: 'li',
32507             cls: 'roo-navigation-bar-item',
32508             cn : [
32509                 {
32510                     tag : 'i',
32511                     cls : iconCls
32512                 }
32513             ]
32514         };
32515         
32516         if(this.active){
32517             cfg.cls += ' active';
32518         }
32519         if(this.disabled){
32520             cfg.cls += ' disabled';
32521         }
32522         
32523         return cfg;
32524     },
32525     
32526     disable : function()
32527     {
32528         this.setDisabled(true);
32529     },
32530     
32531     enable : function()
32532     {
32533         this.setDisabled(false);
32534     },
32535     
32536     initEvents: function() 
32537     {
32538         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32539         
32540         this.iconEl.on('click', this.onClick, this);
32541     },
32542     
32543     onClick : function(e)
32544     {
32545         e.preventDefault();
32546         
32547         if(this.disabled){
32548             return;
32549         }
32550         
32551         if(this.fireEvent('click', this, e) === false){
32552             return;
32553         };
32554         
32555         this.parent().setActiveItem(this);
32556     },
32557     
32558     isActive: function () 
32559     {
32560         return this.active;
32561     },
32562     
32563     setActive : function(state)
32564     {
32565         if(this.active == state){
32566             return;
32567         }
32568         
32569         this.active = state;
32570         
32571         if (state) {
32572             this.el.addClass('active');
32573             return;
32574         }
32575         
32576         this.el.removeClass('active');
32577         
32578         return;
32579     },
32580     
32581     setDisabled : function(state)
32582     {
32583         if(this.disabled == state){
32584             return;
32585         }
32586         
32587         this.disabled = state;
32588         
32589         if (state) {
32590             this.el.addClass('disabled');
32591             return;
32592         }
32593         
32594         this.el.removeClass('disabled');
32595     },
32596     
32597     tooltipEl : function()
32598     {
32599         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32600     }
32601 });
32602  
32603
32604  /*
32605  * - LGPL
32606  *
32607  * FieldLabel
32608  * 
32609  */
32610
32611 /**
32612  * @class Roo.bootstrap.FieldLabel
32613  * @extends Roo.bootstrap.Component
32614  * Bootstrap FieldLabel class
32615  * @cfg {String} html contents of the element
32616  * @cfg {String} tag tag of the element default label
32617  * @cfg {String} cls class of the element
32618  * @cfg {String} target label target 
32619  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32620  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32621  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32622  * @cfg {String} iconTooltip default "This field is required"
32623  * @cfg {String} indicatorpos (left|right) default left
32624  * 
32625  * @constructor
32626  * Create a new FieldLabel
32627  * @param {Object} config The config object
32628  */
32629
32630 Roo.bootstrap.FieldLabel = function(config){
32631     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32632     
32633     this.addEvents({
32634             /**
32635              * @event invalid
32636              * Fires after the field has been marked as invalid.
32637              * @param {Roo.form.FieldLabel} this
32638              * @param {String} msg The validation message
32639              */
32640             invalid : true,
32641             /**
32642              * @event valid
32643              * Fires after the field has been validated with no errors.
32644              * @param {Roo.form.FieldLabel} this
32645              */
32646             valid : true
32647         });
32648 };
32649
32650 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32651     
32652     tag: 'label',
32653     cls: '',
32654     html: '',
32655     target: '',
32656     allowBlank : true,
32657     invalidClass : 'has-warning',
32658     validClass : 'has-success',
32659     iconTooltip : 'This field is required',
32660     indicatorpos : 'left',
32661     
32662     getAutoCreate : function(){
32663         
32664         var cls = "";
32665         if (!this.allowBlank) {
32666             cls  = "visible";
32667         }
32668         
32669         var cfg = {
32670             tag : this.tag,
32671             cls : 'roo-bootstrap-field-label ' + this.cls,
32672             for : this.target,
32673             cn : [
32674                 {
32675                     tag : 'i',
32676                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32677                     tooltip : this.iconTooltip
32678                 },
32679                 {
32680                     tag : 'span',
32681                     html : this.html
32682                 }
32683             ] 
32684         };
32685         
32686         if(this.indicatorpos == 'right'){
32687             var cfg = {
32688                 tag : this.tag,
32689                 cls : 'roo-bootstrap-field-label ' + this.cls,
32690                 for : this.target,
32691                 cn : [
32692                     {
32693                         tag : 'span',
32694                         html : this.html
32695                     },
32696                     {
32697                         tag : 'i',
32698                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32699                         tooltip : this.iconTooltip
32700                     }
32701                 ] 
32702             };
32703         }
32704         
32705         return cfg;
32706     },
32707     
32708     initEvents: function() 
32709     {
32710         Roo.bootstrap.Element.superclass.initEvents.call(this);
32711         
32712         this.indicator = this.indicatorEl();
32713         
32714         if(this.indicator){
32715             this.indicator.removeClass('visible');
32716             this.indicator.addClass('invisible');
32717         }
32718         
32719         Roo.bootstrap.FieldLabel.register(this);
32720     },
32721     
32722     indicatorEl : function()
32723     {
32724         var indicator = this.el.select('i.roo-required-indicator',true).first();
32725         
32726         if(!indicator){
32727             return false;
32728         }
32729         
32730         return indicator;
32731         
32732     },
32733     
32734     /**
32735      * Mark this field as valid
32736      */
32737     markValid : function()
32738     {
32739         if(this.indicator){
32740             this.indicator.removeClass('visible');
32741             this.indicator.addClass('invisible');
32742         }
32743         if (Roo.bootstrap.version == 3) {
32744             this.el.removeClass(this.invalidClass);
32745             this.el.addClass(this.validClass);
32746         } else {
32747             this.el.removeClass('is-invalid');
32748             this.el.addClass('is-valid');
32749         }
32750         
32751         
32752         this.fireEvent('valid', this);
32753     },
32754     
32755     /**
32756      * Mark this field as invalid
32757      * @param {String} msg The validation message
32758      */
32759     markInvalid : function(msg)
32760     {
32761         if(this.indicator){
32762             this.indicator.removeClass('invisible');
32763             this.indicator.addClass('visible');
32764         }
32765           if (Roo.bootstrap.version == 3) {
32766             this.el.removeClass(this.validClass);
32767             this.el.addClass(this.invalidClass);
32768         } else {
32769             this.el.removeClass('is-valid');
32770             this.el.addClass('is-invalid');
32771         }
32772         
32773         
32774         this.fireEvent('invalid', this, msg);
32775     }
32776     
32777    
32778 });
32779
32780 Roo.apply(Roo.bootstrap.FieldLabel, {
32781     
32782     groups: {},
32783     
32784      /**
32785     * register a FieldLabel Group
32786     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32787     */
32788     register : function(label)
32789     {
32790         if(this.groups.hasOwnProperty(label.target)){
32791             return;
32792         }
32793      
32794         this.groups[label.target] = label;
32795         
32796     },
32797     /**
32798     * fetch a FieldLabel Group based on the target
32799     * @param {string} target
32800     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32801     */
32802     get: function(target) {
32803         if (typeof(this.groups[target]) == 'undefined') {
32804             return false;
32805         }
32806         
32807         return this.groups[target] ;
32808     }
32809 });
32810
32811  
32812
32813  /*
32814  * - LGPL
32815  *
32816  * page DateSplitField.
32817  * 
32818  */
32819
32820
32821 /**
32822  * @class Roo.bootstrap.DateSplitField
32823  * @extends Roo.bootstrap.Component
32824  * Bootstrap DateSplitField class
32825  * @cfg {string} fieldLabel - the label associated
32826  * @cfg {Number} labelWidth set the width of label (0-12)
32827  * @cfg {String} labelAlign (top|left)
32828  * @cfg {Boolean} dayAllowBlank (true|false) default false
32829  * @cfg {Boolean} monthAllowBlank (true|false) default false
32830  * @cfg {Boolean} yearAllowBlank (true|false) default false
32831  * @cfg {string} dayPlaceholder 
32832  * @cfg {string} monthPlaceholder
32833  * @cfg {string} yearPlaceholder
32834  * @cfg {string} dayFormat default 'd'
32835  * @cfg {string} monthFormat default 'm'
32836  * @cfg {string} yearFormat default 'Y'
32837  * @cfg {Number} labellg set the width of label (1-12)
32838  * @cfg {Number} labelmd set the width of label (1-12)
32839  * @cfg {Number} labelsm set the width of label (1-12)
32840  * @cfg {Number} labelxs set the width of label (1-12)
32841
32842  *     
32843  * @constructor
32844  * Create a new DateSplitField
32845  * @param {Object} config The config object
32846  */
32847
32848 Roo.bootstrap.DateSplitField = function(config){
32849     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32850     
32851     this.addEvents({
32852         // raw events
32853          /**
32854          * @event years
32855          * getting the data of years
32856          * @param {Roo.bootstrap.DateSplitField} this
32857          * @param {Object} years
32858          */
32859         "years" : true,
32860         /**
32861          * @event days
32862          * getting the data of days
32863          * @param {Roo.bootstrap.DateSplitField} this
32864          * @param {Object} days
32865          */
32866         "days" : true,
32867         /**
32868          * @event invalid
32869          * Fires after the field has been marked as invalid.
32870          * @param {Roo.form.Field} this
32871          * @param {String} msg The validation message
32872          */
32873         invalid : true,
32874        /**
32875          * @event valid
32876          * Fires after the field has been validated with no errors.
32877          * @param {Roo.form.Field} this
32878          */
32879         valid : true
32880     });
32881 };
32882
32883 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32884     
32885     fieldLabel : '',
32886     labelAlign : 'top',
32887     labelWidth : 3,
32888     dayAllowBlank : false,
32889     monthAllowBlank : false,
32890     yearAllowBlank : false,
32891     dayPlaceholder : '',
32892     monthPlaceholder : '',
32893     yearPlaceholder : '',
32894     dayFormat : 'd',
32895     monthFormat : 'm',
32896     yearFormat : 'Y',
32897     isFormField : true,
32898     labellg : 0,
32899     labelmd : 0,
32900     labelsm : 0,
32901     labelxs : 0,
32902     
32903     getAutoCreate : function()
32904     {
32905         var cfg = {
32906             tag : 'div',
32907             cls : 'row roo-date-split-field-group',
32908             cn : [
32909                 {
32910                     tag : 'input',
32911                     type : 'hidden',
32912                     cls : 'form-hidden-field roo-date-split-field-group-value',
32913                     name : this.name
32914                 }
32915             ]
32916         };
32917         
32918         var labelCls = 'col-md-12';
32919         var contentCls = 'col-md-4';
32920         
32921         if(this.fieldLabel){
32922             
32923             var label = {
32924                 tag : 'div',
32925                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32926                 cn : [
32927                     {
32928                         tag : 'label',
32929                         html : this.fieldLabel
32930                     }
32931                 ]
32932             };
32933             
32934             if(this.labelAlign == 'left'){
32935             
32936                 if(this.labelWidth > 12){
32937                     label.style = "width: " + this.labelWidth + 'px';
32938                 }
32939
32940                 if(this.labelWidth < 13 && this.labelmd == 0){
32941                     this.labelmd = this.labelWidth;
32942                 }
32943
32944                 if(this.labellg > 0){
32945                     labelCls = ' col-lg-' + this.labellg;
32946                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32947                 }
32948
32949                 if(this.labelmd > 0){
32950                     labelCls = ' col-md-' + this.labelmd;
32951                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32952                 }
32953
32954                 if(this.labelsm > 0){
32955                     labelCls = ' col-sm-' + this.labelsm;
32956                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32957                 }
32958
32959                 if(this.labelxs > 0){
32960                     labelCls = ' col-xs-' + this.labelxs;
32961                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32962                 }
32963             }
32964             
32965             label.cls += ' ' + labelCls;
32966             
32967             cfg.cn.push(label);
32968         }
32969         
32970         Roo.each(['day', 'month', 'year'], function(t){
32971             cfg.cn.push({
32972                 tag : 'div',
32973                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32974             });
32975         }, this);
32976         
32977         return cfg;
32978     },
32979     
32980     inputEl: function ()
32981     {
32982         return this.el.select('.roo-date-split-field-group-value', true).first();
32983     },
32984     
32985     onRender : function(ct, position) 
32986     {
32987         var _this = this;
32988         
32989         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32990         
32991         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32992         
32993         this.dayField = new Roo.bootstrap.ComboBox({
32994             allowBlank : this.dayAllowBlank,
32995             alwaysQuery : true,
32996             displayField : 'value',
32997             editable : false,
32998             fieldLabel : '',
32999             forceSelection : true,
33000             mode : 'local',
33001             placeholder : this.dayPlaceholder,
33002             selectOnFocus : true,
33003             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33004             triggerAction : 'all',
33005             typeAhead : true,
33006             valueField : 'value',
33007             store : new Roo.data.SimpleStore({
33008                 data : (function() {    
33009                     var days = [];
33010                     _this.fireEvent('days', _this, days);
33011                     return days;
33012                 })(),
33013                 fields : [ 'value' ]
33014             }),
33015             listeners : {
33016                 select : function (_self, record, index)
33017                 {
33018                     _this.setValue(_this.getValue());
33019                 }
33020             }
33021         });
33022
33023         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33024         
33025         this.monthField = new Roo.bootstrap.MonthField({
33026             after : '<i class=\"fa fa-calendar\"></i>',
33027             allowBlank : this.monthAllowBlank,
33028             placeholder : this.monthPlaceholder,
33029             readOnly : true,
33030             listeners : {
33031                 render : function (_self)
33032                 {
33033                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33034                         e.preventDefault();
33035                         _self.focus();
33036                     });
33037                 },
33038                 select : function (_self, oldvalue, newvalue)
33039                 {
33040                     _this.setValue(_this.getValue());
33041                 }
33042             }
33043         });
33044         
33045         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33046         
33047         this.yearField = new Roo.bootstrap.ComboBox({
33048             allowBlank : this.yearAllowBlank,
33049             alwaysQuery : true,
33050             displayField : 'value',
33051             editable : false,
33052             fieldLabel : '',
33053             forceSelection : true,
33054             mode : 'local',
33055             placeholder : this.yearPlaceholder,
33056             selectOnFocus : true,
33057             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33058             triggerAction : 'all',
33059             typeAhead : true,
33060             valueField : 'value',
33061             store : new Roo.data.SimpleStore({
33062                 data : (function() {
33063                     var years = [];
33064                     _this.fireEvent('years', _this, years);
33065                     return years;
33066                 })(),
33067                 fields : [ 'value' ]
33068             }),
33069             listeners : {
33070                 select : function (_self, record, index)
33071                 {
33072                     _this.setValue(_this.getValue());
33073                 }
33074             }
33075         });
33076
33077         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33078     },
33079     
33080     setValue : function(v, format)
33081     {
33082         this.inputEl.dom.value = v;
33083         
33084         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33085         
33086         var d = Date.parseDate(v, f);
33087         
33088         if(!d){
33089             this.validate();
33090             return;
33091         }
33092         
33093         this.setDay(d.format(this.dayFormat));
33094         this.setMonth(d.format(this.monthFormat));
33095         this.setYear(d.format(this.yearFormat));
33096         
33097         this.validate();
33098         
33099         return;
33100     },
33101     
33102     setDay : function(v)
33103     {
33104         this.dayField.setValue(v);
33105         this.inputEl.dom.value = this.getValue();
33106         this.validate();
33107         return;
33108     },
33109     
33110     setMonth : function(v)
33111     {
33112         this.monthField.setValue(v, true);
33113         this.inputEl.dom.value = this.getValue();
33114         this.validate();
33115         return;
33116     },
33117     
33118     setYear : function(v)
33119     {
33120         this.yearField.setValue(v);
33121         this.inputEl.dom.value = this.getValue();
33122         this.validate();
33123         return;
33124     },
33125     
33126     getDay : function()
33127     {
33128         return this.dayField.getValue();
33129     },
33130     
33131     getMonth : function()
33132     {
33133         return this.monthField.getValue();
33134     },
33135     
33136     getYear : function()
33137     {
33138         return this.yearField.getValue();
33139     },
33140     
33141     getValue : function()
33142     {
33143         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33144         
33145         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33146         
33147         return date;
33148     },
33149     
33150     reset : function()
33151     {
33152         this.setDay('');
33153         this.setMonth('');
33154         this.setYear('');
33155         this.inputEl.dom.value = '';
33156         this.validate();
33157         return;
33158     },
33159     
33160     validate : function()
33161     {
33162         var d = this.dayField.validate();
33163         var m = this.monthField.validate();
33164         var y = this.yearField.validate();
33165         
33166         var valid = true;
33167         
33168         if(
33169                 (!this.dayAllowBlank && !d) ||
33170                 (!this.monthAllowBlank && !m) ||
33171                 (!this.yearAllowBlank && !y)
33172         ){
33173             valid = false;
33174         }
33175         
33176         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33177             return valid;
33178         }
33179         
33180         if(valid){
33181             this.markValid();
33182             return valid;
33183         }
33184         
33185         this.markInvalid();
33186         
33187         return valid;
33188     },
33189     
33190     markValid : function()
33191     {
33192         
33193         var label = this.el.select('label', true).first();
33194         var icon = this.el.select('i.fa-star', true).first();
33195
33196         if(label && icon){
33197             icon.remove();
33198         }
33199         
33200         this.fireEvent('valid', this);
33201     },
33202     
33203      /**
33204      * Mark this field as invalid
33205      * @param {String} msg The validation message
33206      */
33207     markInvalid : function(msg)
33208     {
33209         
33210         var label = this.el.select('label', true).first();
33211         var icon = this.el.select('i.fa-star', true).first();
33212
33213         if(label && !icon){
33214             this.el.select('.roo-date-split-field-label', true).createChild({
33215                 tag : 'i',
33216                 cls : 'text-danger fa fa-lg fa-star',
33217                 tooltip : 'This field is required',
33218                 style : 'margin-right:5px;'
33219             }, label, true);
33220         }
33221         
33222         this.fireEvent('invalid', this, msg);
33223     },
33224     
33225     clearInvalid : function()
33226     {
33227         var label = this.el.select('label', true).first();
33228         var icon = this.el.select('i.fa-star', true).first();
33229
33230         if(label && icon){
33231             icon.remove();
33232         }
33233         
33234         this.fireEvent('valid', this);
33235     },
33236     
33237     getName: function()
33238     {
33239         return this.name;
33240     }
33241     
33242 });
33243
33244  /**
33245  *
33246  * This is based on 
33247  * http://masonry.desandro.com
33248  *
33249  * The idea is to render all the bricks based on vertical width...
33250  *
33251  * The original code extends 'outlayer' - we might need to use that....
33252  * 
33253  */
33254
33255
33256 /**
33257  * @class Roo.bootstrap.LayoutMasonry
33258  * @extends Roo.bootstrap.Component
33259  * Bootstrap Layout Masonry class
33260  * 
33261  * @constructor
33262  * Create a new Element
33263  * @param {Object} config The config object
33264  */
33265
33266 Roo.bootstrap.LayoutMasonry = function(config){
33267     
33268     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33269     
33270     this.bricks = [];
33271     
33272     Roo.bootstrap.LayoutMasonry.register(this);
33273     
33274     this.addEvents({
33275         // raw events
33276         /**
33277          * @event layout
33278          * Fire after layout the items
33279          * @param {Roo.bootstrap.LayoutMasonry} this
33280          * @param {Roo.EventObject} e
33281          */
33282         "layout" : true
33283     });
33284     
33285 };
33286
33287 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33288     
33289     /**
33290      * @cfg {Boolean} isLayoutInstant = no animation?
33291      */   
33292     isLayoutInstant : false, // needed?
33293    
33294     /**
33295      * @cfg {Number} boxWidth  width of the columns
33296      */   
33297     boxWidth : 450,
33298     
33299       /**
33300      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33301      */   
33302     boxHeight : 0,
33303     
33304     /**
33305      * @cfg {Number} padWidth padding below box..
33306      */   
33307     padWidth : 10, 
33308     
33309     /**
33310      * @cfg {Number} gutter gutter width..
33311      */   
33312     gutter : 10,
33313     
33314      /**
33315      * @cfg {Number} maxCols maximum number of columns
33316      */   
33317     
33318     maxCols: 0,
33319     
33320     /**
33321      * @cfg {Boolean} isAutoInitial defalut true
33322      */   
33323     isAutoInitial : true, 
33324     
33325     containerWidth: 0,
33326     
33327     /**
33328      * @cfg {Boolean} isHorizontal defalut false
33329      */   
33330     isHorizontal : false, 
33331
33332     currentSize : null,
33333     
33334     tag: 'div',
33335     
33336     cls: '',
33337     
33338     bricks: null, //CompositeElement
33339     
33340     cols : 1,
33341     
33342     _isLayoutInited : false,
33343     
33344 //    isAlternative : false, // only use for vertical layout...
33345     
33346     /**
33347      * @cfg {Number} alternativePadWidth padding below box..
33348      */   
33349     alternativePadWidth : 50,
33350     
33351     selectedBrick : [],
33352     
33353     getAutoCreate : function(){
33354         
33355         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33356         
33357         var cfg = {
33358             tag: this.tag,
33359             cls: 'blog-masonary-wrapper ' + this.cls,
33360             cn : {
33361                 cls : 'mas-boxes masonary'
33362             }
33363         };
33364         
33365         return cfg;
33366     },
33367     
33368     getChildContainer: function( )
33369     {
33370         if (this.boxesEl) {
33371             return this.boxesEl;
33372         }
33373         
33374         this.boxesEl = this.el.select('.mas-boxes').first();
33375         
33376         return this.boxesEl;
33377     },
33378     
33379     
33380     initEvents : function()
33381     {
33382         var _this = this;
33383         
33384         if(this.isAutoInitial){
33385             Roo.log('hook children rendered');
33386             this.on('childrenrendered', function() {
33387                 Roo.log('children rendered');
33388                 _this.initial();
33389             } ,this);
33390         }
33391     },
33392     
33393     initial : function()
33394     {
33395         this.selectedBrick = [];
33396         
33397         this.currentSize = this.el.getBox(true);
33398         
33399         Roo.EventManager.onWindowResize(this.resize, this); 
33400
33401         if(!this.isAutoInitial){
33402             this.layout();
33403             return;
33404         }
33405         
33406         this.layout();
33407         
33408         return;
33409         //this.layout.defer(500,this);
33410         
33411     },
33412     
33413     resize : function()
33414     {
33415         var cs = this.el.getBox(true);
33416         
33417         if (
33418                 this.currentSize.width == cs.width && 
33419                 this.currentSize.x == cs.x && 
33420                 this.currentSize.height == cs.height && 
33421                 this.currentSize.y == cs.y 
33422         ) {
33423             Roo.log("no change in with or X or Y");
33424             return;
33425         }
33426         
33427         this.currentSize = cs;
33428         
33429         this.layout();
33430         
33431     },
33432     
33433     layout : function()
33434     {   
33435         this._resetLayout();
33436         
33437         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33438         
33439         this.layoutItems( isInstant );
33440       
33441         this._isLayoutInited = true;
33442         
33443         this.fireEvent('layout', this);
33444         
33445     },
33446     
33447     _resetLayout : function()
33448     {
33449         if(this.isHorizontal){
33450             this.horizontalMeasureColumns();
33451             return;
33452         }
33453         
33454         this.verticalMeasureColumns();
33455         
33456     },
33457     
33458     verticalMeasureColumns : function()
33459     {
33460         this.getContainerWidth();
33461         
33462 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33463 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33464 //            return;
33465 //        }
33466         
33467         var boxWidth = this.boxWidth + this.padWidth;
33468         
33469         if(this.containerWidth < this.boxWidth){
33470             boxWidth = this.containerWidth
33471         }
33472         
33473         var containerWidth = this.containerWidth;
33474         
33475         var cols = Math.floor(containerWidth / boxWidth);
33476         
33477         this.cols = Math.max( cols, 1 );
33478         
33479         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33480         
33481         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33482         
33483         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33484         
33485         this.colWidth = boxWidth + avail - this.padWidth;
33486         
33487         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33488         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33489     },
33490     
33491     horizontalMeasureColumns : function()
33492     {
33493         this.getContainerWidth();
33494         
33495         var boxWidth = this.boxWidth;
33496         
33497         if(this.containerWidth < boxWidth){
33498             boxWidth = this.containerWidth;
33499         }
33500         
33501         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33502         
33503         this.el.setHeight(boxWidth);
33504         
33505     },
33506     
33507     getContainerWidth : function()
33508     {
33509         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33510     },
33511     
33512     layoutItems : function( isInstant )
33513     {
33514         Roo.log(this.bricks);
33515         
33516         var items = Roo.apply([], this.bricks);
33517         
33518         if(this.isHorizontal){
33519             this._horizontalLayoutItems( items , isInstant );
33520             return;
33521         }
33522         
33523 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33524 //            this._verticalAlternativeLayoutItems( items , isInstant );
33525 //            return;
33526 //        }
33527         
33528         this._verticalLayoutItems( items , isInstant );
33529         
33530     },
33531     
33532     _verticalLayoutItems : function ( items , isInstant)
33533     {
33534         if ( !items || !items.length ) {
33535             return;
33536         }
33537         
33538         var standard = [
33539             ['xs', 'xs', 'xs', 'tall'],
33540             ['xs', 'xs', 'tall'],
33541             ['xs', 'xs', 'sm'],
33542             ['xs', 'xs', 'xs'],
33543             ['xs', 'tall'],
33544             ['xs', 'sm'],
33545             ['xs', 'xs'],
33546             ['xs'],
33547             
33548             ['sm', 'xs', 'xs'],
33549             ['sm', 'xs'],
33550             ['sm'],
33551             
33552             ['tall', 'xs', 'xs', 'xs'],
33553             ['tall', 'xs', 'xs'],
33554             ['tall', 'xs'],
33555             ['tall']
33556             
33557         ];
33558         
33559         var queue = [];
33560         
33561         var boxes = [];
33562         
33563         var box = [];
33564         
33565         Roo.each(items, function(item, k){
33566             
33567             switch (item.size) {
33568                 // these layouts take up a full box,
33569                 case 'md' :
33570                 case 'md-left' :
33571                 case 'md-right' :
33572                 case 'wide' :
33573                     
33574                     if(box.length){
33575                         boxes.push(box);
33576                         box = [];
33577                     }
33578                     
33579                     boxes.push([item]);
33580                     
33581                     break;
33582                     
33583                 case 'xs' :
33584                 case 'sm' :
33585                 case 'tall' :
33586                     
33587                     box.push(item);
33588                     
33589                     break;
33590                 default :
33591                     break;
33592                     
33593             }
33594             
33595         }, this);
33596         
33597         if(box.length){
33598             boxes.push(box);
33599             box = [];
33600         }
33601         
33602         var filterPattern = function(box, length)
33603         {
33604             if(!box.length){
33605                 return;
33606             }
33607             
33608             var match = false;
33609             
33610             var pattern = box.slice(0, length);
33611             
33612             var format = [];
33613             
33614             Roo.each(pattern, function(i){
33615                 format.push(i.size);
33616             }, this);
33617             
33618             Roo.each(standard, function(s){
33619                 
33620                 if(String(s) != String(format)){
33621                     return;
33622                 }
33623                 
33624                 match = true;
33625                 return false;
33626                 
33627             }, this);
33628             
33629             if(!match && length == 1){
33630                 return;
33631             }
33632             
33633             if(!match){
33634                 filterPattern(box, length - 1);
33635                 return;
33636             }
33637                 
33638             queue.push(pattern);
33639
33640             box = box.slice(length, box.length);
33641
33642             filterPattern(box, 4);
33643
33644             return;
33645             
33646         }
33647         
33648         Roo.each(boxes, function(box, k){
33649             
33650             if(!box.length){
33651                 return;
33652             }
33653             
33654             if(box.length == 1){
33655                 queue.push(box);
33656                 return;
33657             }
33658             
33659             filterPattern(box, 4);
33660             
33661         }, this);
33662         
33663         this._processVerticalLayoutQueue( queue, isInstant );
33664         
33665     },
33666     
33667 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33668 //    {
33669 //        if ( !items || !items.length ) {
33670 //            return;
33671 //        }
33672 //
33673 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33674 //        
33675 //    },
33676     
33677     _horizontalLayoutItems : function ( items , isInstant)
33678     {
33679         if ( !items || !items.length || items.length < 3) {
33680             return;
33681         }
33682         
33683         items.reverse();
33684         
33685         var eItems = items.slice(0, 3);
33686         
33687         items = items.slice(3, items.length);
33688         
33689         var standard = [
33690             ['xs', 'xs', 'xs', 'wide'],
33691             ['xs', 'xs', 'wide'],
33692             ['xs', 'xs', 'sm'],
33693             ['xs', 'xs', 'xs'],
33694             ['xs', 'wide'],
33695             ['xs', 'sm'],
33696             ['xs', 'xs'],
33697             ['xs'],
33698             
33699             ['sm', 'xs', 'xs'],
33700             ['sm', 'xs'],
33701             ['sm'],
33702             
33703             ['wide', 'xs', 'xs', 'xs'],
33704             ['wide', 'xs', 'xs'],
33705             ['wide', 'xs'],
33706             ['wide'],
33707             
33708             ['wide-thin']
33709         ];
33710         
33711         var queue = [];
33712         
33713         var boxes = [];
33714         
33715         var box = [];
33716         
33717         Roo.each(items, function(item, k){
33718             
33719             switch (item.size) {
33720                 case 'md' :
33721                 case 'md-left' :
33722                 case 'md-right' :
33723                 case 'tall' :
33724                     
33725                     if(box.length){
33726                         boxes.push(box);
33727                         box = [];
33728                     }
33729                     
33730                     boxes.push([item]);
33731                     
33732                     break;
33733                     
33734                 case 'xs' :
33735                 case 'sm' :
33736                 case 'wide' :
33737                 case 'wide-thin' :
33738                     
33739                     box.push(item);
33740                     
33741                     break;
33742                 default :
33743                     break;
33744                     
33745             }
33746             
33747         }, this);
33748         
33749         if(box.length){
33750             boxes.push(box);
33751             box = [];
33752         }
33753         
33754         var filterPattern = function(box, length)
33755         {
33756             if(!box.length){
33757                 return;
33758             }
33759             
33760             var match = false;
33761             
33762             var pattern = box.slice(0, length);
33763             
33764             var format = [];
33765             
33766             Roo.each(pattern, function(i){
33767                 format.push(i.size);
33768             }, this);
33769             
33770             Roo.each(standard, function(s){
33771                 
33772                 if(String(s) != String(format)){
33773                     return;
33774                 }
33775                 
33776                 match = true;
33777                 return false;
33778                 
33779             }, this);
33780             
33781             if(!match && length == 1){
33782                 return;
33783             }
33784             
33785             if(!match){
33786                 filterPattern(box, length - 1);
33787                 return;
33788             }
33789                 
33790             queue.push(pattern);
33791
33792             box = box.slice(length, box.length);
33793
33794             filterPattern(box, 4);
33795
33796             return;
33797             
33798         }
33799         
33800         Roo.each(boxes, function(box, k){
33801             
33802             if(!box.length){
33803                 return;
33804             }
33805             
33806             if(box.length == 1){
33807                 queue.push(box);
33808                 return;
33809             }
33810             
33811             filterPattern(box, 4);
33812             
33813         }, this);
33814         
33815         
33816         var prune = [];
33817         
33818         var pos = this.el.getBox(true);
33819         
33820         var minX = pos.x;
33821         
33822         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33823         
33824         var hit_end = false;
33825         
33826         Roo.each(queue, function(box){
33827             
33828             if(hit_end){
33829                 
33830                 Roo.each(box, function(b){
33831                 
33832                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33833                     b.el.hide();
33834
33835                 }, this);
33836
33837                 return;
33838             }
33839             
33840             var mx = 0;
33841             
33842             Roo.each(box, function(b){
33843                 
33844                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33845                 b.el.show();
33846
33847                 mx = Math.max(mx, b.x);
33848                 
33849             }, this);
33850             
33851             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33852             
33853             if(maxX < minX){
33854                 
33855                 Roo.each(box, function(b){
33856                 
33857                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33858                     b.el.hide();
33859                     
33860                 }, this);
33861                 
33862                 hit_end = true;
33863                 
33864                 return;
33865             }
33866             
33867             prune.push(box);
33868             
33869         }, this);
33870         
33871         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33872     },
33873     
33874     /** Sets position of item in DOM
33875     * @param {Element} item
33876     * @param {Number} x - horizontal position
33877     * @param {Number} y - vertical position
33878     * @param {Boolean} isInstant - disables transitions
33879     */
33880     _processVerticalLayoutQueue : function( queue, isInstant )
33881     {
33882         var pos = this.el.getBox(true);
33883         var x = pos.x;
33884         var y = pos.y;
33885         var maxY = [];
33886         
33887         for (var i = 0; i < this.cols; i++){
33888             maxY[i] = pos.y;
33889         }
33890         
33891         Roo.each(queue, function(box, k){
33892             
33893             var col = k % this.cols;
33894             
33895             Roo.each(box, function(b,kk){
33896                 
33897                 b.el.position('absolute');
33898                 
33899                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33900                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33901                 
33902                 if(b.size == 'md-left' || b.size == 'md-right'){
33903                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33904                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33905                 }
33906                 
33907                 b.el.setWidth(width);
33908                 b.el.setHeight(height);
33909                 // iframe?
33910                 b.el.select('iframe',true).setSize(width,height);
33911                 
33912             }, this);
33913             
33914             for (var i = 0; i < this.cols; i++){
33915                 
33916                 if(maxY[i] < maxY[col]){
33917                     col = i;
33918                     continue;
33919                 }
33920                 
33921                 col = Math.min(col, i);
33922                 
33923             }
33924             
33925             x = pos.x + col * (this.colWidth + this.padWidth);
33926             
33927             y = maxY[col];
33928             
33929             var positions = [];
33930             
33931             switch (box.length){
33932                 case 1 :
33933                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33934                     break;
33935                 case 2 :
33936                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33937                     break;
33938                 case 3 :
33939                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33940                     break;
33941                 case 4 :
33942                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33943                     break;
33944                 default :
33945                     break;
33946             }
33947             
33948             Roo.each(box, function(b,kk){
33949                 
33950                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33951                 
33952                 var sz = b.el.getSize();
33953                 
33954                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33955                 
33956             }, this);
33957             
33958         }, this);
33959         
33960         var mY = 0;
33961         
33962         for (var i = 0; i < this.cols; i++){
33963             mY = Math.max(mY, maxY[i]);
33964         }
33965         
33966         this.el.setHeight(mY - pos.y);
33967         
33968     },
33969     
33970 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33971 //    {
33972 //        var pos = this.el.getBox(true);
33973 //        var x = pos.x;
33974 //        var y = pos.y;
33975 //        var maxX = pos.right;
33976 //        
33977 //        var maxHeight = 0;
33978 //        
33979 //        Roo.each(items, function(item, k){
33980 //            
33981 //            var c = k % 2;
33982 //            
33983 //            item.el.position('absolute');
33984 //                
33985 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33986 //
33987 //            item.el.setWidth(width);
33988 //
33989 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33990 //
33991 //            item.el.setHeight(height);
33992 //            
33993 //            if(c == 0){
33994 //                item.el.setXY([x, y], isInstant ? false : true);
33995 //            } else {
33996 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33997 //            }
33998 //            
33999 //            y = y + height + this.alternativePadWidth;
34000 //            
34001 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34002 //            
34003 //        }, this);
34004 //        
34005 //        this.el.setHeight(maxHeight);
34006 //        
34007 //    },
34008     
34009     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34010     {
34011         var pos = this.el.getBox(true);
34012         
34013         var minX = pos.x;
34014         var minY = pos.y;
34015         
34016         var maxX = pos.right;
34017         
34018         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34019         
34020         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34021         
34022         Roo.each(queue, function(box, k){
34023             
34024             Roo.each(box, function(b, kk){
34025                 
34026                 b.el.position('absolute');
34027                 
34028                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34029                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34030                 
34031                 if(b.size == 'md-left' || b.size == 'md-right'){
34032                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34033                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34034                 }
34035                 
34036                 b.el.setWidth(width);
34037                 b.el.setHeight(height);
34038                 
34039             }, this);
34040             
34041             if(!box.length){
34042                 return;
34043             }
34044             
34045             var positions = [];
34046             
34047             switch (box.length){
34048                 case 1 :
34049                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34050                     break;
34051                 case 2 :
34052                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34053                     break;
34054                 case 3 :
34055                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34056                     break;
34057                 case 4 :
34058                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34059                     break;
34060                 default :
34061                     break;
34062             }
34063             
34064             Roo.each(box, function(b,kk){
34065                 
34066                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34067                 
34068                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34069                 
34070             }, this);
34071             
34072         }, this);
34073         
34074     },
34075     
34076     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34077     {
34078         Roo.each(eItems, function(b,k){
34079             
34080             b.size = (k == 0) ? 'sm' : 'xs';
34081             b.x = (k == 0) ? 2 : 1;
34082             b.y = (k == 0) ? 2 : 1;
34083             
34084             b.el.position('absolute');
34085             
34086             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34087                 
34088             b.el.setWidth(width);
34089             
34090             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34091             
34092             b.el.setHeight(height);
34093             
34094         }, this);
34095
34096         var positions = [];
34097         
34098         positions.push({
34099             x : maxX - this.unitWidth * 2 - this.gutter,
34100             y : minY
34101         });
34102         
34103         positions.push({
34104             x : maxX - this.unitWidth,
34105             y : minY + (this.unitWidth + this.gutter) * 2
34106         });
34107         
34108         positions.push({
34109             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34110             y : minY
34111         });
34112         
34113         Roo.each(eItems, function(b,k){
34114             
34115             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34116
34117         }, this);
34118         
34119     },
34120     
34121     getVerticalOneBoxColPositions : function(x, y, box)
34122     {
34123         var pos = [];
34124         
34125         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34126         
34127         if(box[0].size == 'md-left'){
34128             rand = 0;
34129         }
34130         
34131         if(box[0].size == 'md-right'){
34132             rand = 1;
34133         }
34134         
34135         pos.push({
34136             x : x + (this.unitWidth + this.gutter) * rand,
34137             y : y
34138         });
34139         
34140         return pos;
34141     },
34142     
34143     getVerticalTwoBoxColPositions : function(x, y, box)
34144     {
34145         var pos = [];
34146         
34147         if(box[0].size == 'xs'){
34148             
34149             pos.push({
34150                 x : x,
34151                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34152             });
34153
34154             pos.push({
34155                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34156                 y : y
34157             });
34158             
34159             return pos;
34160             
34161         }
34162         
34163         pos.push({
34164             x : x,
34165             y : y
34166         });
34167
34168         pos.push({
34169             x : x + (this.unitWidth + this.gutter) * 2,
34170             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34171         });
34172         
34173         return pos;
34174         
34175     },
34176     
34177     getVerticalThreeBoxColPositions : function(x, y, box)
34178     {
34179         var pos = [];
34180         
34181         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34182             
34183             pos.push({
34184                 x : x,
34185                 y : y
34186             });
34187
34188             pos.push({
34189                 x : x + (this.unitWidth + this.gutter) * 1,
34190                 y : y
34191             });
34192             
34193             pos.push({
34194                 x : x + (this.unitWidth + this.gutter) * 2,
34195                 y : y
34196             });
34197             
34198             return pos;
34199             
34200         }
34201         
34202         if(box[0].size == 'xs' && box[1].size == 'xs'){
34203             
34204             pos.push({
34205                 x : x,
34206                 y : y
34207             });
34208
34209             pos.push({
34210                 x : x,
34211                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34212             });
34213             
34214             pos.push({
34215                 x : x + (this.unitWidth + this.gutter) * 1,
34216                 y : y
34217             });
34218             
34219             return pos;
34220             
34221         }
34222         
34223         pos.push({
34224             x : x,
34225             y : y
34226         });
34227
34228         pos.push({
34229             x : x + (this.unitWidth + this.gutter) * 2,
34230             y : y
34231         });
34232
34233         pos.push({
34234             x : x + (this.unitWidth + this.gutter) * 2,
34235             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34236         });
34237             
34238         return pos;
34239         
34240     },
34241     
34242     getVerticalFourBoxColPositions : function(x, y, box)
34243     {
34244         var pos = [];
34245         
34246         if(box[0].size == 'xs'){
34247             
34248             pos.push({
34249                 x : x,
34250                 y : y
34251             });
34252
34253             pos.push({
34254                 x : x,
34255                 y : y + (this.unitHeight + this.gutter) * 1
34256             });
34257             
34258             pos.push({
34259                 x : x,
34260                 y : y + (this.unitHeight + this.gutter) * 2
34261             });
34262             
34263             pos.push({
34264                 x : x + (this.unitWidth + this.gutter) * 1,
34265                 y : y
34266             });
34267             
34268             return pos;
34269             
34270         }
34271         
34272         pos.push({
34273             x : x,
34274             y : y
34275         });
34276
34277         pos.push({
34278             x : x + (this.unitWidth + this.gutter) * 2,
34279             y : y
34280         });
34281
34282         pos.push({
34283             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34284             y : y + (this.unitHeight + this.gutter) * 1
34285         });
34286
34287         pos.push({
34288             x : x + (this.unitWidth + this.gutter) * 2,
34289             y : y + (this.unitWidth + this.gutter) * 2
34290         });
34291
34292         return pos;
34293         
34294     },
34295     
34296     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34297     {
34298         var pos = [];
34299         
34300         if(box[0].size == 'md-left'){
34301             pos.push({
34302                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34303                 y : minY
34304             });
34305             
34306             return pos;
34307         }
34308         
34309         if(box[0].size == 'md-right'){
34310             pos.push({
34311                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34312                 y : minY + (this.unitWidth + this.gutter) * 1
34313             });
34314             
34315             return pos;
34316         }
34317         
34318         var rand = Math.floor(Math.random() * (4 - box[0].y));
34319         
34320         pos.push({
34321             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34322             y : minY + (this.unitWidth + this.gutter) * rand
34323         });
34324         
34325         return pos;
34326         
34327     },
34328     
34329     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34330     {
34331         var pos = [];
34332         
34333         if(box[0].size == 'xs'){
34334             
34335             pos.push({
34336                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34337                 y : minY
34338             });
34339
34340             pos.push({
34341                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34342                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34343             });
34344             
34345             return pos;
34346             
34347         }
34348         
34349         pos.push({
34350             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34351             y : minY
34352         });
34353
34354         pos.push({
34355             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34356             y : minY + (this.unitWidth + this.gutter) * 2
34357         });
34358         
34359         return pos;
34360         
34361     },
34362     
34363     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34364     {
34365         var pos = [];
34366         
34367         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34368             
34369             pos.push({
34370                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34371                 y : minY
34372             });
34373
34374             pos.push({
34375                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34376                 y : minY + (this.unitWidth + this.gutter) * 1
34377             });
34378             
34379             pos.push({
34380                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34381                 y : minY + (this.unitWidth + this.gutter) * 2
34382             });
34383             
34384             return pos;
34385             
34386         }
34387         
34388         if(box[0].size == 'xs' && box[1].size == 'xs'){
34389             
34390             pos.push({
34391                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34392                 y : minY
34393             });
34394
34395             pos.push({
34396                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34397                 y : minY
34398             });
34399             
34400             pos.push({
34401                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34402                 y : minY + (this.unitWidth + this.gutter) * 1
34403             });
34404             
34405             return pos;
34406             
34407         }
34408         
34409         pos.push({
34410             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34411             y : minY
34412         });
34413
34414         pos.push({
34415             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34416             y : minY + (this.unitWidth + this.gutter) * 2
34417         });
34418
34419         pos.push({
34420             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34421             y : minY + (this.unitWidth + this.gutter) * 2
34422         });
34423             
34424         return pos;
34425         
34426     },
34427     
34428     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34429     {
34430         var pos = [];
34431         
34432         if(box[0].size == 'xs'){
34433             
34434             pos.push({
34435                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34436                 y : minY
34437             });
34438
34439             pos.push({
34440                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34441                 y : minY
34442             });
34443             
34444             pos.push({
34445                 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),
34446                 y : minY
34447             });
34448             
34449             pos.push({
34450                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34451                 y : minY + (this.unitWidth + this.gutter) * 1
34452             });
34453             
34454             return pos;
34455             
34456         }
34457         
34458         pos.push({
34459             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34460             y : minY
34461         });
34462         
34463         pos.push({
34464             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34465             y : minY + (this.unitWidth + this.gutter) * 2
34466         });
34467         
34468         pos.push({
34469             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34470             y : minY + (this.unitWidth + this.gutter) * 2
34471         });
34472         
34473         pos.push({
34474             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),
34475             y : minY + (this.unitWidth + this.gutter) * 2
34476         });
34477
34478         return pos;
34479         
34480     },
34481     
34482     /**
34483     * remove a Masonry Brick
34484     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34485     */
34486     removeBrick : function(brick_id)
34487     {
34488         if (!brick_id) {
34489             return;
34490         }
34491         
34492         for (var i = 0; i<this.bricks.length; i++) {
34493             if (this.bricks[i].id == brick_id) {
34494                 this.bricks.splice(i,1);
34495                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34496                 this.initial();
34497             }
34498         }
34499     },
34500     
34501     /**
34502     * adds a Masonry Brick
34503     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34504     */
34505     addBrick : function(cfg)
34506     {
34507         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34508         //this.register(cn);
34509         cn.parentId = this.id;
34510         cn.render(this.el);
34511         return cn;
34512     },
34513     
34514     /**
34515     * register a Masonry Brick
34516     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34517     */
34518     
34519     register : function(brick)
34520     {
34521         this.bricks.push(brick);
34522         brick.masonryId = this.id;
34523     },
34524     
34525     /**
34526     * clear all the Masonry Brick
34527     */
34528     clearAll : function()
34529     {
34530         this.bricks = [];
34531         //this.getChildContainer().dom.innerHTML = "";
34532         this.el.dom.innerHTML = '';
34533     },
34534     
34535     getSelected : function()
34536     {
34537         if (!this.selectedBrick) {
34538             return false;
34539         }
34540         
34541         return this.selectedBrick;
34542     }
34543 });
34544
34545 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34546     
34547     groups: {},
34548      /**
34549     * register a Masonry Layout
34550     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34551     */
34552     
34553     register : function(layout)
34554     {
34555         this.groups[layout.id] = layout;
34556     },
34557     /**
34558     * fetch a  Masonry Layout based on the masonry layout ID
34559     * @param {string} the masonry layout to add
34560     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34561     */
34562     
34563     get: function(layout_id) {
34564         if (typeof(this.groups[layout_id]) == 'undefined') {
34565             return false;
34566         }
34567         return this.groups[layout_id] ;
34568     }
34569     
34570     
34571     
34572 });
34573
34574  
34575
34576  /**
34577  *
34578  * This is based on 
34579  * http://masonry.desandro.com
34580  *
34581  * The idea is to render all the bricks based on vertical width...
34582  *
34583  * The original code extends 'outlayer' - we might need to use that....
34584  * 
34585  */
34586
34587
34588 /**
34589  * @class Roo.bootstrap.LayoutMasonryAuto
34590  * @extends Roo.bootstrap.Component
34591  * Bootstrap Layout Masonry class
34592  * 
34593  * @constructor
34594  * Create a new Element
34595  * @param {Object} config The config object
34596  */
34597
34598 Roo.bootstrap.LayoutMasonryAuto = function(config){
34599     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34600 };
34601
34602 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34603     
34604       /**
34605      * @cfg {Boolean} isFitWidth  - resize the width..
34606      */   
34607     isFitWidth : false,  // options..
34608     /**
34609      * @cfg {Boolean} isOriginLeft = left align?
34610      */   
34611     isOriginLeft : true,
34612     /**
34613      * @cfg {Boolean} isOriginTop = top align?
34614      */   
34615     isOriginTop : false,
34616     /**
34617      * @cfg {Boolean} isLayoutInstant = no animation?
34618      */   
34619     isLayoutInstant : false, // needed?
34620     /**
34621      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34622      */   
34623     isResizingContainer : true,
34624     /**
34625      * @cfg {Number} columnWidth  width of the columns 
34626      */   
34627     
34628     columnWidth : 0,
34629     
34630     /**
34631      * @cfg {Number} maxCols maximum number of columns
34632      */   
34633     
34634     maxCols: 0,
34635     /**
34636      * @cfg {Number} padHeight padding below box..
34637      */   
34638     
34639     padHeight : 10, 
34640     
34641     /**
34642      * @cfg {Boolean} isAutoInitial defalut true
34643      */   
34644     
34645     isAutoInitial : true, 
34646     
34647     // private?
34648     gutter : 0,
34649     
34650     containerWidth: 0,
34651     initialColumnWidth : 0,
34652     currentSize : null,
34653     
34654     colYs : null, // array.
34655     maxY : 0,
34656     padWidth: 10,
34657     
34658     
34659     tag: 'div',
34660     cls: '',
34661     bricks: null, //CompositeElement
34662     cols : 0, // array?
34663     // element : null, // wrapped now this.el
34664     _isLayoutInited : null, 
34665     
34666     
34667     getAutoCreate : function(){
34668         
34669         var cfg = {
34670             tag: this.tag,
34671             cls: 'blog-masonary-wrapper ' + this.cls,
34672             cn : {
34673                 cls : 'mas-boxes masonary'
34674             }
34675         };
34676         
34677         return cfg;
34678     },
34679     
34680     getChildContainer: function( )
34681     {
34682         if (this.boxesEl) {
34683             return this.boxesEl;
34684         }
34685         
34686         this.boxesEl = this.el.select('.mas-boxes').first();
34687         
34688         return this.boxesEl;
34689     },
34690     
34691     
34692     initEvents : function()
34693     {
34694         var _this = this;
34695         
34696         if(this.isAutoInitial){
34697             Roo.log('hook children rendered');
34698             this.on('childrenrendered', function() {
34699                 Roo.log('children rendered');
34700                 _this.initial();
34701             } ,this);
34702         }
34703         
34704     },
34705     
34706     initial : function()
34707     {
34708         this.reloadItems();
34709
34710         this.currentSize = this.el.getBox(true);
34711
34712         /// was window resize... - let's see if this works..
34713         Roo.EventManager.onWindowResize(this.resize, this); 
34714
34715         if(!this.isAutoInitial){
34716             this.layout();
34717             return;
34718         }
34719         
34720         this.layout.defer(500,this);
34721     },
34722     
34723     reloadItems: function()
34724     {
34725         this.bricks = this.el.select('.masonry-brick', true);
34726         
34727         this.bricks.each(function(b) {
34728             //Roo.log(b.getSize());
34729             if (!b.attr('originalwidth')) {
34730                 b.attr('originalwidth',  b.getSize().width);
34731             }
34732             
34733         });
34734         
34735         Roo.log(this.bricks.elements.length);
34736     },
34737     
34738     resize : function()
34739     {
34740         Roo.log('resize');
34741         var cs = this.el.getBox(true);
34742         
34743         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34744             Roo.log("no change in with or X");
34745             return;
34746         }
34747         this.currentSize = cs;
34748         this.layout();
34749     },
34750     
34751     layout : function()
34752     {
34753          Roo.log('layout');
34754         this._resetLayout();
34755         //this._manageStamps();
34756       
34757         // don't animate first layout
34758         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34759         this.layoutItems( isInstant );
34760       
34761         // flag for initalized
34762         this._isLayoutInited = true;
34763     },
34764     
34765     layoutItems : function( isInstant )
34766     {
34767         //var items = this._getItemsForLayout( this.items );
34768         // original code supports filtering layout items.. we just ignore it..
34769         
34770         this._layoutItems( this.bricks , isInstant );
34771       
34772         this._postLayout();
34773     },
34774     _layoutItems : function ( items , isInstant)
34775     {
34776        //this.fireEvent( 'layout', this, items );
34777     
34778
34779         if ( !items || !items.elements.length ) {
34780           // no items, emit event with empty array
34781             return;
34782         }
34783
34784         var queue = [];
34785         items.each(function(item) {
34786             Roo.log("layout item");
34787             Roo.log(item);
34788             // get x/y object from method
34789             var position = this._getItemLayoutPosition( item );
34790             // enqueue
34791             position.item = item;
34792             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34793             queue.push( position );
34794         }, this);
34795       
34796         this._processLayoutQueue( queue );
34797     },
34798     /** Sets position of item in DOM
34799     * @param {Element} item
34800     * @param {Number} x - horizontal position
34801     * @param {Number} y - vertical position
34802     * @param {Boolean} isInstant - disables transitions
34803     */
34804     _processLayoutQueue : function( queue )
34805     {
34806         for ( var i=0, len = queue.length; i < len; i++ ) {
34807             var obj = queue[i];
34808             obj.item.position('absolute');
34809             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34810         }
34811     },
34812       
34813     
34814     /**
34815     * Any logic you want to do after each layout,
34816     * i.e. size the container
34817     */
34818     _postLayout : function()
34819     {
34820         this.resizeContainer();
34821     },
34822     
34823     resizeContainer : function()
34824     {
34825         if ( !this.isResizingContainer ) {
34826             return;
34827         }
34828         var size = this._getContainerSize();
34829         if ( size ) {
34830             this.el.setSize(size.width,size.height);
34831             this.boxesEl.setSize(size.width,size.height);
34832         }
34833     },
34834     
34835     
34836     
34837     _resetLayout : function()
34838     {
34839         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34840         this.colWidth = this.el.getWidth();
34841         //this.gutter = this.el.getWidth(); 
34842         
34843         this.measureColumns();
34844
34845         // reset column Y
34846         var i = this.cols;
34847         this.colYs = [];
34848         while (i--) {
34849             this.colYs.push( 0 );
34850         }
34851     
34852         this.maxY = 0;
34853     },
34854
34855     measureColumns : function()
34856     {
34857         this.getContainerWidth();
34858       // if columnWidth is 0, default to outerWidth of first item
34859         if ( !this.columnWidth ) {
34860             var firstItem = this.bricks.first();
34861             Roo.log(firstItem);
34862             this.columnWidth  = this.containerWidth;
34863             if (firstItem && firstItem.attr('originalwidth') ) {
34864                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34865             }
34866             // columnWidth fall back to item of first element
34867             Roo.log("set column width?");
34868                         this.initialColumnWidth = this.columnWidth  ;
34869
34870             // if first elem has no width, default to size of container
34871             
34872         }
34873         
34874         
34875         if (this.initialColumnWidth) {
34876             this.columnWidth = this.initialColumnWidth;
34877         }
34878         
34879         
34880             
34881         // column width is fixed at the top - however if container width get's smaller we should
34882         // reduce it...
34883         
34884         // this bit calcs how man columns..
34885             
34886         var columnWidth = this.columnWidth += this.gutter;
34887       
34888         // calculate columns
34889         var containerWidth = this.containerWidth + this.gutter;
34890         
34891         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34892         // fix rounding errors, typically with gutters
34893         var excess = columnWidth - containerWidth % columnWidth;
34894         
34895         
34896         // if overshoot is less than a pixel, round up, otherwise floor it
34897         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34898         cols = Math[ mathMethod ]( cols );
34899         this.cols = Math.max( cols, 1 );
34900         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34901         
34902          // padding positioning..
34903         var totalColWidth = this.cols * this.columnWidth;
34904         var padavail = this.containerWidth - totalColWidth;
34905         // so for 2 columns - we need 3 'pads'
34906         
34907         var padNeeded = (1+this.cols) * this.padWidth;
34908         
34909         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34910         
34911         this.columnWidth += padExtra
34912         //this.padWidth = Math.floor(padavail /  ( this.cols));
34913         
34914         // adjust colum width so that padding is fixed??
34915         
34916         // we have 3 columns ... total = width * 3
34917         // we have X left over... that should be used by 
34918         
34919         //if (this.expandC) {
34920             
34921         //}
34922         
34923         
34924         
34925     },
34926     
34927     getContainerWidth : function()
34928     {
34929        /* // container is parent if fit width
34930         var container = this.isFitWidth ? this.element.parentNode : this.element;
34931         // check that this.size and size are there
34932         // IE8 triggers resize on body size change, so they might not be
34933         
34934         var size = getSize( container );  //FIXME
34935         this.containerWidth = size && size.innerWidth; //FIXME
34936         */
34937          
34938         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34939         
34940     },
34941     
34942     _getItemLayoutPosition : function( item )  // what is item?
34943     {
34944         // we resize the item to our columnWidth..
34945       
34946         item.setWidth(this.columnWidth);
34947         item.autoBoxAdjust  = false;
34948         
34949         var sz = item.getSize();
34950  
34951         // how many columns does this brick span
34952         var remainder = this.containerWidth % this.columnWidth;
34953         
34954         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34955         // round if off by 1 pixel, otherwise use ceil
34956         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34957         colSpan = Math.min( colSpan, this.cols );
34958         
34959         // normally this should be '1' as we dont' currently allow multi width columns..
34960         
34961         var colGroup = this._getColGroup( colSpan );
34962         // get the minimum Y value from the columns
34963         var minimumY = Math.min.apply( Math, colGroup );
34964         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34965         
34966         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34967          
34968         // position the brick
34969         var position = {
34970             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34971             y: this.currentSize.y + minimumY + this.padHeight
34972         };
34973         
34974         Roo.log(position);
34975         // apply setHeight to necessary columns
34976         var setHeight = minimumY + sz.height + this.padHeight;
34977         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34978         
34979         var setSpan = this.cols + 1 - colGroup.length;
34980         for ( var i = 0; i < setSpan; i++ ) {
34981           this.colYs[ shortColIndex + i ] = setHeight ;
34982         }
34983       
34984         return position;
34985     },
34986     
34987     /**
34988      * @param {Number} colSpan - number of columns the element spans
34989      * @returns {Array} colGroup
34990      */
34991     _getColGroup : function( colSpan )
34992     {
34993         if ( colSpan < 2 ) {
34994           // if brick spans only one column, use all the column Ys
34995           return this.colYs;
34996         }
34997       
34998         var colGroup = [];
34999         // how many different places could this brick fit horizontally
35000         var groupCount = this.cols + 1 - colSpan;
35001         // for each group potential horizontal position
35002         for ( var i = 0; i < groupCount; i++ ) {
35003           // make an array of colY values for that one group
35004           var groupColYs = this.colYs.slice( i, i + colSpan );
35005           // and get the max value of the array
35006           colGroup[i] = Math.max.apply( Math, groupColYs );
35007         }
35008         return colGroup;
35009     },
35010     /*
35011     _manageStamp : function( stamp )
35012     {
35013         var stampSize =  stamp.getSize();
35014         var offset = stamp.getBox();
35015         // get the columns that this stamp affects
35016         var firstX = this.isOriginLeft ? offset.x : offset.right;
35017         var lastX = firstX + stampSize.width;
35018         var firstCol = Math.floor( firstX / this.columnWidth );
35019         firstCol = Math.max( 0, firstCol );
35020         
35021         var lastCol = Math.floor( lastX / this.columnWidth );
35022         // lastCol should not go over if multiple of columnWidth #425
35023         lastCol -= lastX % this.columnWidth ? 0 : 1;
35024         lastCol = Math.min( this.cols - 1, lastCol );
35025         
35026         // set colYs to bottom of the stamp
35027         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35028             stampSize.height;
35029             
35030         for ( var i = firstCol; i <= lastCol; i++ ) {
35031           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35032         }
35033     },
35034     */
35035     
35036     _getContainerSize : function()
35037     {
35038         this.maxY = Math.max.apply( Math, this.colYs );
35039         var size = {
35040             height: this.maxY
35041         };
35042       
35043         if ( this.isFitWidth ) {
35044             size.width = this._getContainerFitWidth();
35045         }
35046       
35047         return size;
35048     },
35049     
35050     _getContainerFitWidth : function()
35051     {
35052         var unusedCols = 0;
35053         // count unused columns
35054         var i = this.cols;
35055         while ( --i ) {
35056           if ( this.colYs[i] !== 0 ) {
35057             break;
35058           }
35059           unusedCols++;
35060         }
35061         // fit container to columns that have been used
35062         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35063     },
35064     
35065     needsResizeLayout : function()
35066     {
35067         var previousWidth = this.containerWidth;
35068         this.getContainerWidth();
35069         return previousWidth !== this.containerWidth;
35070     }
35071  
35072 });
35073
35074  
35075
35076  /*
35077  * - LGPL
35078  *
35079  * element
35080  * 
35081  */
35082
35083 /**
35084  * @class Roo.bootstrap.MasonryBrick
35085  * @extends Roo.bootstrap.Component
35086  * Bootstrap MasonryBrick class
35087  * 
35088  * @constructor
35089  * Create a new MasonryBrick
35090  * @param {Object} config The config object
35091  */
35092
35093 Roo.bootstrap.MasonryBrick = function(config){
35094     
35095     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35096     
35097     Roo.bootstrap.MasonryBrick.register(this);
35098     
35099     this.addEvents({
35100         // raw events
35101         /**
35102          * @event click
35103          * When a MasonryBrick is clcik
35104          * @param {Roo.bootstrap.MasonryBrick} this
35105          * @param {Roo.EventObject} e
35106          */
35107         "click" : true
35108     });
35109 };
35110
35111 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35112     
35113     /**
35114      * @cfg {String} title
35115      */   
35116     title : '',
35117     /**
35118      * @cfg {String} html
35119      */   
35120     html : '',
35121     /**
35122      * @cfg {String} bgimage
35123      */   
35124     bgimage : '',
35125     /**
35126      * @cfg {String} videourl
35127      */   
35128     videourl : '',
35129     /**
35130      * @cfg {String} cls
35131      */   
35132     cls : '',
35133     /**
35134      * @cfg {String} href
35135      */   
35136     href : '',
35137     /**
35138      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35139      */   
35140     size : 'xs',
35141     
35142     /**
35143      * @cfg {String} placetitle (center|bottom)
35144      */   
35145     placetitle : '',
35146     
35147     /**
35148      * @cfg {Boolean} isFitContainer defalut true
35149      */   
35150     isFitContainer : true, 
35151     
35152     /**
35153      * @cfg {Boolean} preventDefault defalut false
35154      */   
35155     preventDefault : false, 
35156     
35157     /**
35158      * @cfg {Boolean} inverse defalut false
35159      */   
35160     maskInverse : false, 
35161     
35162     getAutoCreate : function()
35163     {
35164         if(!this.isFitContainer){
35165             return this.getSplitAutoCreate();
35166         }
35167         
35168         var cls = 'masonry-brick masonry-brick-full';
35169         
35170         if(this.href.length){
35171             cls += ' masonry-brick-link';
35172         }
35173         
35174         if(this.bgimage.length){
35175             cls += ' masonry-brick-image';
35176         }
35177         
35178         if(this.maskInverse){
35179             cls += ' mask-inverse';
35180         }
35181         
35182         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35183             cls += ' enable-mask';
35184         }
35185         
35186         if(this.size){
35187             cls += ' masonry-' + this.size + '-brick';
35188         }
35189         
35190         if(this.placetitle.length){
35191             
35192             switch (this.placetitle) {
35193                 case 'center' :
35194                     cls += ' masonry-center-title';
35195                     break;
35196                 case 'bottom' :
35197                     cls += ' masonry-bottom-title';
35198                     break;
35199                 default:
35200                     break;
35201             }
35202             
35203         } else {
35204             if(!this.html.length && !this.bgimage.length){
35205                 cls += ' masonry-center-title';
35206             }
35207
35208             if(!this.html.length && this.bgimage.length){
35209                 cls += ' masonry-bottom-title';
35210             }
35211         }
35212         
35213         if(this.cls){
35214             cls += ' ' + this.cls;
35215         }
35216         
35217         var cfg = {
35218             tag: (this.href.length) ? 'a' : 'div',
35219             cls: cls,
35220             cn: [
35221                 {
35222                     tag: 'div',
35223                     cls: 'masonry-brick-mask'
35224                 },
35225                 {
35226                     tag: 'div',
35227                     cls: 'masonry-brick-paragraph',
35228                     cn: []
35229                 }
35230             ]
35231         };
35232         
35233         if(this.href.length){
35234             cfg.href = this.href;
35235         }
35236         
35237         var cn = cfg.cn[1].cn;
35238         
35239         if(this.title.length){
35240             cn.push({
35241                 tag: 'h4',
35242                 cls: 'masonry-brick-title',
35243                 html: this.title
35244             });
35245         }
35246         
35247         if(this.html.length){
35248             cn.push({
35249                 tag: 'p',
35250                 cls: 'masonry-brick-text',
35251                 html: this.html
35252             });
35253         }
35254         
35255         if (!this.title.length && !this.html.length) {
35256             cfg.cn[1].cls += ' hide';
35257         }
35258         
35259         if(this.bgimage.length){
35260             cfg.cn.push({
35261                 tag: 'img',
35262                 cls: 'masonry-brick-image-view',
35263                 src: this.bgimage
35264             });
35265         }
35266         
35267         if(this.videourl.length){
35268             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35269             // youtube support only?
35270             cfg.cn.push({
35271                 tag: 'iframe',
35272                 cls: 'masonry-brick-image-view',
35273                 src: vurl,
35274                 frameborder : 0,
35275                 allowfullscreen : true
35276             });
35277         }
35278         
35279         return cfg;
35280         
35281     },
35282     
35283     getSplitAutoCreate : function()
35284     {
35285         var cls = 'masonry-brick masonry-brick-split';
35286         
35287         if(this.href.length){
35288             cls += ' masonry-brick-link';
35289         }
35290         
35291         if(this.bgimage.length){
35292             cls += ' masonry-brick-image';
35293         }
35294         
35295         if(this.size){
35296             cls += ' masonry-' + this.size + '-brick';
35297         }
35298         
35299         switch (this.placetitle) {
35300             case 'center' :
35301                 cls += ' masonry-center-title';
35302                 break;
35303             case 'bottom' :
35304                 cls += ' masonry-bottom-title';
35305                 break;
35306             default:
35307                 if(!this.bgimage.length){
35308                     cls += ' masonry-center-title';
35309                 }
35310
35311                 if(this.bgimage.length){
35312                     cls += ' masonry-bottom-title';
35313                 }
35314                 break;
35315         }
35316         
35317         if(this.cls){
35318             cls += ' ' + this.cls;
35319         }
35320         
35321         var cfg = {
35322             tag: (this.href.length) ? 'a' : 'div',
35323             cls: cls,
35324             cn: [
35325                 {
35326                     tag: 'div',
35327                     cls: 'masonry-brick-split-head',
35328                     cn: [
35329                         {
35330                             tag: 'div',
35331                             cls: 'masonry-brick-paragraph',
35332                             cn: []
35333                         }
35334                     ]
35335                 },
35336                 {
35337                     tag: 'div',
35338                     cls: 'masonry-brick-split-body',
35339                     cn: []
35340                 }
35341             ]
35342         };
35343         
35344         if(this.href.length){
35345             cfg.href = this.href;
35346         }
35347         
35348         if(this.title.length){
35349             cfg.cn[0].cn[0].cn.push({
35350                 tag: 'h4',
35351                 cls: 'masonry-brick-title',
35352                 html: this.title
35353             });
35354         }
35355         
35356         if(this.html.length){
35357             cfg.cn[1].cn.push({
35358                 tag: 'p',
35359                 cls: 'masonry-brick-text',
35360                 html: this.html
35361             });
35362         }
35363
35364         if(this.bgimage.length){
35365             cfg.cn[0].cn.push({
35366                 tag: 'img',
35367                 cls: 'masonry-brick-image-view',
35368                 src: this.bgimage
35369             });
35370         }
35371         
35372         if(this.videourl.length){
35373             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35374             // youtube support only?
35375             cfg.cn[0].cn.cn.push({
35376                 tag: 'iframe',
35377                 cls: 'masonry-brick-image-view',
35378                 src: vurl,
35379                 frameborder : 0,
35380                 allowfullscreen : true
35381             });
35382         }
35383         
35384         return cfg;
35385     },
35386     
35387     initEvents: function() 
35388     {
35389         switch (this.size) {
35390             case 'xs' :
35391                 this.x = 1;
35392                 this.y = 1;
35393                 break;
35394             case 'sm' :
35395                 this.x = 2;
35396                 this.y = 2;
35397                 break;
35398             case 'md' :
35399             case 'md-left' :
35400             case 'md-right' :
35401                 this.x = 3;
35402                 this.y = 3;
35403                 break;
35404             case 'tall' :
35405                 this.x = 2;
35406                 this.y = 3;
35407                 break;
35408             case 'wide' :
35409                 this.x = 3;
35410                 this.y = 2;
35411                 break;
35412             case 'wide-thin' :
35413                 this.x = 3;
35414                 this.y = 1;
35415                 break;
35416                         
35417             default :
35418                 break;
35419         }
35420         
35421         if(Roo.isTouch){
35422             this.el.on('touchstart', this.onTouchStart, this);
35423             this.el.on('touchmove', this.onTouchMove, this);
35424             this.el.on('touchend', this.onTouchEnd, this);
35425             this.el.on('contextmenu', this.onContextMenu, this);
35426         } else {
35427             this.el.on('mouseenter'  ,this.enter, this);
35428             this.el.on('mouseleave', this.leave, this);
35429             this.el.on('click', this.onClick, this);
35430         }
35431         
35432         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35433             this.parent().bricks.push(this);   
35434         }
35435         
35436     },
35437     
35438     onClick: function(e, el)
35439     {
35440         var time = this.endTimer - this.startTimer;
35441         // Roo.log(e.preventDefault());
35442         if(Roo.isTouch){
35443             if(time > 1000){
35444                 e.preventDefault();
35445                 return;
35446             }
35447         }
35448         
35449         if(!this.preventDefault){
35450             return;
35451         }
35452         
35453         e.preventDefault();
35454         
35455         if (this.activeClass != '') {
35456             this.selectBrick();
35457         }
35458         
35459         this.fireEvent('click', this, e);
35460     },
35461     
35462     enter: function(e, el)
35463     {
35464         e.preventDefault();
35465         
35466         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35467             return;
35468         }
35469         
35470         if(this.bgimage.length && this.html.length){
35471             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35472         }
35473     },
35474     
35475     leave: function(e, el)
35476     {
35477         e.preventDefault();
35478         
35479         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35480             return;
35481         }
35482         
35483         if(this.bgimage.length && this.html.length){
35484             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35485         }
35486     },
35487     
35488     onTouchStart: function(e, el)
35489     {
35490 //        e.preventDefault();
35491         
35492         this.touchmoved = false;
35493         
35494         if(!this.isFitContainer){
35495             return;
35496         }
35497         
35498         if(!this.bgimage.length || !this.html.length){
35499             return;
35500         }
35501         
35502         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35503         
35504         this.timer = new Date().getTime();
35505         
35506     },
35507     
35508     onTouchMove: function(e, el)
35509     {
35510         this.touchmoved = true;
35511     },
35512     
35513     onContextMenu : function(e,el)
35514     {
35515         e.preventDefault();
35516         e.stopPropagation();
35517         return false;
35518     },
35519     
35520     onTouchEnd: function(e, el)
35521     {
35522 //        e.preventDefault();
35523         
35524         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35525         
35526             this.leave(e,el);
35527             
35528             return;
35529         }
35530         
35531         if(!this.bgimage.length || !this.html.length){
35532             
35533             if(this.href.length){
35534                 window.location.href = this.href;
35535             }
35536             
35537             return;
35538         }
35539         
35540         if(!this.isFitContainer){
35541             return;
35542         }
35543         
35544         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35545         
35546         window.location.href = this.href;
35547     },
35548     
35549     //selection on single brick only
35550     selectBrick : function() {
35551         
35552         if (!this.parentId) {
35553             return;
35554         }
35555         
35556         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35557         var index = m.selectedBrick.indexOf(this.id);
35558         
35559         if ( index > -1) {
35560             m.selectedBrick.splice(index,1);
35561             this.el.removeClass(this.activeClass);
35562             return;
35563         }
35564         
35565         for(var i = 0; i < m.selectedBrick.length; i++) {
35566             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35567             b.el.removeClass(b.activeClass);
35568         }
35569         
35570         m.selectedBrick = [];
35571         
35572         m.selectedBrick.push(this.id);
35573         this.el.addClass(this.activeClass);
35574         return;
35575     },
35576     
35577     isSelected : function(){
35578         return this.el.hasClass(this.activeClass);
35579         
35580     }
35581 });
35582
35583 Roo.apply(Roo.bootstrap.MasonryBrick, {
35584     
35585     //groups: {},
35586     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35587      /**
35588     * register a Masonry Brick
35589     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35590     */
35591     
35592     register : function(brick)
35593     {
35594         //this.groups[brick.id] = brick;
35595         this.groups.add(brick.id, brick);
35596     },
35597     /**
35598     * fetch a  masonry brick based on the masonry brick ID
35599     * @param {string} the masonry brick to add
35600     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35601     */
35602     
35603     get: function(brick_id) 
35604     {
35605         // if (typeof(this.groups[brick_id]) == 'undefined') {
35606         //     return false;
35607         // }
35608         // return this.groups[brick_id] ;
35609         
35610         if(this.groups.key(brick_id)) {
35611             return this.groups.key(brick_id);
35612         }
35613         
35614         return false;
35615     }
35616     
35617     
35618     
35619 });
35620
35621  /*
35622  * - LGPL
35623  *
35624  * element
35625  * 
35626  */
35627
35628 /**
35629  * @class Roo.bootstrap.Brick
35630  * @extends Roo.bootstrap.Component
35631  * Bootstrap Brick class
35632  * 
35633  * @constructor
35634  * Create a new Brick
35635  * @param {Object} config The config object
35636  */
35637
35638 Roo.bootstrap.Brick = function(config){
35639     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35640     
35641     this.addEvents({
35642         // raw events
35643         /**
35644          * @event click
35645          * When a Brick is click
35646          * @param {Roo.bootstrap.Brick} this
35647          * @param {Roo.EventObject} e
35648          */
35649         "click" : true
35650     });
35651 };
35652
35653 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35654     
35655     /**
35656      * @cfg {String} title
35657      */   
35658     title : '',
35659     /**
35660      * @cfg {String} html
35661      */   
35662     html : '',
35663     /**
35664      * @cfg {String} bgimage
35665      */   
35666     bgimage : '',
35667     /**
35668      * @cfg {String} cls
35669      */   
35670     cls : '',
35671     /**
35672      * @cfg {String} href
35673      */   
35674     href : '',
35675     /**
35676      * @cfg {String} video
35677      */   
35678     video : '',
35679     /**
35680      * @cfg {Boolean} square
35681      */   
35682     square : true,
35683     
35684     getAutoCreate : function()
35685     {
35686         var cls = 'roo-brick';
35687         
35688         if(this.href.length){
35689             cls += ' roo-brick-link';
35690         }
35691         
35692         if(this.bgimage.length){
35693             cls += ' roo-brick-image';
35694         }
35695         
35696         if(!this.html.length && !this.bgimage.length){
35697             cls += ' roo-brick-center-title';
35698         }
35699         
35700         if(!this.html.length && this.bgimage.length){
35701             cls += ' roo-brick-bottom-title';
35702         }
35703         
35704         if(this.cls){
35705             cls += ' ' + this.cls;
35706         }
35707         
35708         var cfg = {
35709             tag: (this.href.length) ? 'a' : 'div',
35710             cls: cls,
35711             cn: [
35712                 {
35713                     tag: 'div',
35714                     cls: 'roo-brick-paragraph',
35715                     cn: []
35716                 }
35717             ]
35718         };
35719         
35720         if(this.href.length){
35721             cfg.href = this.href;
35722         }
35723         
35724         var cn = cfg.cn[0].cn;
35725         
35726         if(this.title.length){
35727             cn.push({
35728                 tag: 'h4',
35729                 cls: 'roo-brick-title',
35730                 html: this.title
35731             });
35732         }
35733         
35734         if(this.html.length){
35735             cn.push({
35736                 tag: 'p',
35737                 cls: 'roo-brick-text',
35738                 html: this.html
35739             });
35740         } else {
35741             cn.cls += ' hide';
35742         }
35743         
35744         if(this.bgimage.length){
35745             cfg.cn.push({
35746                 tag: 'img',
35747                 cls: 'roo-brick-image-view',
35748                 src: this.bgimage
35749             });
35750         }
35751         
35752         return cfg;
35753     },
35754     
35755     initEvents: function() 
35756     {
35757         if(this.title.length || this.html.length){
35758             this.el.on('mouseenter'  ,this.enter, this);
35759             this.el.on('mouseleave', this.leave, this);
35760         }
35761         
35762         Roo.EventManager.onWindowResize(this.resize, this); 
35763         
35764         if(this.bgimage.length){
35765             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35766             this.imageEl.on('load', this.onImageLoad, this);
35767             return;
35768         }
35769         
35770         this.resize();
35771     },
35772     
35773     onImageLoad : function()
35774     {
35775         this.resize();
35776     },
35777     
35778     resize : function()
35779     {
35780         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35781         
35782         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35783         
35784         if(this.bgimage.length){
35785             var image = this.el.select('.roo-brick-image-view', true).first();
35786             
35787             image.setWidth(paragraph.getWidth());
35788             
35789             if(this.square){
35790                 image.setHeight(paragraph.getWidth());
35791             }
35792             
35793             this.el.setHeight(image.getHeight());
35794             paragraph.setHeight(image.getHeight());
35795             
35796         }
35797         
35798     },
35799     
35800     enter: function(e, el)
35801     {
35802         e.preventDefault();
35803         
35804         if(this.bgimage.length){
35805             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35806             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35807         }
35808     },
35809     
35810     leave: function(e, el)
35811     {
35812         e.preventDefault();
35813         
35814         if(this.bgimage.length){
35815             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35816             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35817         }
35818     }
35819     
35820 });
35821
35822  
35823
35824  /*
35825  * - LGPL
35826  *
35827  * Number field 
35828  */
35829
35830 /**
35831  * @class Roo.bootstrap.NumberField
35832  * @extends Roo.bootstrap.Input
35833  * Bootstrap NumberField class
35834  * 
35835  * 
35836  * 
35837  * 
35838  * @constructor
35839  * Create a new NumberField
35840  * @param {Object} config The config object
35841  */
35842
35843 Roo.bootstrap.NumberField = function(config){
35844     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35845 };
35846
35847 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35848     
35849     /**
35850      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35851      */
35852     allowDecimals : true,
35853     /**
35854      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35855      */
35856     decimalSeparator : ".",
35857     /**
35858      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35859      */
35860     decimalPrecision : 2,
35861     /**
35862      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35863      */
35864     allowNegative : true,
35865     
35866     /**
35867      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35868      */
35869     allowZero: true,
35870     /**
35871      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35872      */
35873     minValue : Number.NEGATIVE_INFINITY,
35874     /**
35875      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35876      */
35877     maxValue : Number.MAX_VALUE,
35878     /**
35879      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35880      */
35881     minText : "The minimum value for this field is {0}",
35882     /**
35883      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35884      */
35885     maxText : "The maximum value for this field is {0}",
35886     /**
35887      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35888      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35889      */
35890     nanText : "{0} is not a valid number",
35891     /**
35892      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35893      */
35894     thousandsDelimiter : false,
35895     /**
35896      * @cfg {String} valueAlign alignment of value
35897      */
35898     valueAlign : "left",
35899
35900     getAutoCreate : function()
35901     {
35902         var hiddenInput = {
35903             tag: 'input',
35904             type: 'hidden',
35905             id: Roo.id(),
35906             cls: 'hidden-number-input'
35907         };
35908         
35909         if (this.name) {
35910             hiddenInput.name = this.name;
35911         }
35912         
35913         this.name = '';
35914         
35915         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35916         
35917         this.name = hiddenInput.name;
35918         
35919         if(cfg.cn.length > 0) {
35920             cfg.cn.push(hiddenInput);
35921         }
35922         
35923         return cfg;
35924     },
35925
35926     // private
35927     initEvents : function()
35928     {   
35929         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35930         
35931         var allowed = "0123456789";
35932         
35933         if(this.allowDecimals){
35934             allowed += this.decimalSeparator;
35935         }
35936         
35937         if(this.allowNegative){
35938             allowed += "-";
35939         }
35940         
35941         if(this.thousandsDelimiter) {
35942             allowed += ",";
35943         }
35944         
35945         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35946         
35947         var keyPress = function(e){
35948             
35949             var k = e.getKey();
35950             
35951             var c = e.getCharCode();
35952             
35953             if(
35954                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35955                     allowed.indexOf(String.fromCharCode(c)) === -1
35956             ){
35957                 e.stopEvent();
35958                 return;
35959             }
35960             
35961             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35962                 return;
35963             }
35964             
35965             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35966                 e.stopEvent();
35967             }
35968         };
35969         
35970         this.el.on("keypress", keyPress, this);
35971     },
35972     
35973     validateValue : function(value)
35974     {
35975         
35976         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35977             return false;
35978         }
35979         
35980         var num = this.parseValue(value);
35981         
35982         if(isNaN(num)){
35983             this.markInvalid(String.format(this.nanText, value));
35984             return false;
35985         }
35986         
35987         if(num < this.minValue){
35988             this.markInvalid(String.format(this.minText, this.minValue));
35989             return false;
35990         }
35991         
35992         if(num > this.maxValue){
35993             this.markInvalid(String.format(this.maxText, this.maxValue));
35994             return false;
35995         }
35996         
35997         return true;
35998     },
35999
36000     getValue : function()
36001     {
36002         var v = this.hiddenEl().getValue();
36003         
36004         return this.fixPrecision(this.parseValue(v));
36005     },
36006
36007     parseValue : function(value)
36008     {
36009         if(this.thousandsDelimiter) {
36010             value += "";
36011             r = new RegExp(",", "g");
36012             value = value.replace(r, "");
36013         }
36014         
36015         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36016         return isNaN(value) ? '' : value;
36017     },
36018
36019     fixPrecision : function(value)
36020     {
36021         if(this.thousandsDelimiter) {
36022             value += "";
36023             r = new RegExp(",", "g");
36024             value = value.replace(r, "");
36025         }
36026         
36027         var nan = isNaN(value);
36028         
36029         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36030             return nan ? '' : value;
36031         }
36032         return parseFloat(value).toFixed(this.decimalPrecision);
36033     },
36034
36035     setValue : function(v)
36036     {
36037         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36038         
36039         this.value = v;
36040         
36041         if(this.rendered){
36042             
36043             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36044             
36045             this.inputEl().dom.value = (v == '') ? '' :
36046                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36047             
36048             if(!this.allowZero && v === '0') {
36049                 this.hiddenEl().dom.value = '';
36050                 this.inputEl().dom.value = '';
36051             }
36052             
36053             this.validate();
36054         }
36055     },
36056
36057     decimalPrecisionFcn : function(v)
36058     {
36059         return Math.floor(v);
36060     },
36061
36062     beforeBlur : function()
36063     {
36064         var v = this.parseValue(this.getRawValue());
36065         
36066         if(v || v === 0 || v === ''){
36067             this.setValue(v);
36068         }
36069     },
36070     
36071     hiddenEl : function()
36072     {
36073         return this.el.select('input.hidden-number-input',true).first();
36074     }
36075     
36076 });
36077
36078  
36079
36080 /*
36081 * Licence: LGPL
36082 */
36083
36084 /**
36085  * @class Roo.bootstrap.DocumentSlider
36086  * @extends Roo.bootstrap.Component
36087  * Bootstrap DocumentSlider class
36088  * 
36089  * @constructor
36090  * Create a new DocumentViewer
36091  * @param {Object} config The config object
36092  */
36093
36094 Roo.bootstrap.DocumentSlider = function(config){
36095     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36096     
36097     this.files = [];
36098     
36099     this.addEvents({
36100         /**
36101          * @event initial
36102          * Fire after initEvent
36103          * @param {Roo.bootstrap.DocumentSlider} this
36104          */
36105         "initial" : true,
36106         /**
36107          * @event update
36108          * Fire after update
36109          * @param {Roo.bootstrap.DocumentSlider} this
36110          */
36111         "update" : true,
36112         /**
36113          * @event click
36114          * Fire after click
36115          * @param {Roo.bootstrap.DocumentSlider} this
36116          */
36117         "click" : true
36118     });
36119 };
36120
36121 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36122     
36123     files : false,
36124     
36125     indicator : 0,
36126     
36127     getAutoCreate : function()
36128     {
36129         var cfg = {
36130             tag : 'div',
36131             cls : 'roo-document-slider',
36132             cn : [
36133                 {
36134                     tag : 'div',
36135                     cls : 'roo-document-slider-header',
36136                     cn : [
36137                         {
36138                             tag : 'div',
36139                             cls : 'roo-document-slider-header-title'
36140                         }
36141                     ]
36142                 },
36143                 {
36144                     tag : 'div',
36145                     cls : 'roo-document-slider-body',
36146                     cn : [
36147                         {
36148                             tag : 'div',
36149                             cls : 'roo-document-slider-prev',
36150                             cn : [
36151                                 {
36152                                     tag : 'i',
36153                                     cls : 'fa fa-chevron-left'
36154                                 }
36155                             ]
36156                         },
36157                         {
36158                             tag : 'div',
36159                             cls : 'roo-document-slider-thumb',
36160                             cn : [
36161                                 {
36162                                     tag : 'img',
36163                                     cls : 'roo-document-slider-image'
36164                                 }
36165                             ]
36166                         },
36167                         {
36168                             tag : 'div',
36169                             cls : 'roo-document-slider-next',
36170                             cn : [
36171                                 {
36172                                     tag : 'i',
36173                                     cls : 'fa fa-chevron-right'
36174                                 }
36175                             ]
36176                         }
36177                     ]
36178                 }
36179             ]
36180         };
36181         
36182         return cfg;
36183     },
36184     
36185     initEvents : function()
36186     {
36187         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36188         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36189         
36190         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36191         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36192         
36193         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36194         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36195         
36196         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36197         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36198         
36199         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36200         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36201         
36202         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36203         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36204         
36205         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36206         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36207         
36208         this.thumbEl.on('click', this.onClick, this);
36209         
36210         this.prevIndicator.on('click', this.prev, this);
36211         
36212         this.nextIndicator.on('click', this.next, this);
36213         
36214     },
36215     
36216     initial : function()
36217     {
36218         if(this.files.length){
36219             this.indicator = 1;
36220             this.update()
36221         }
36222         
36223         this.fireEvent('initial', this);
36224     },
36225     
36226     update : function()
36227     {
36228         this.imageEl.attr('src', this.files[this.indicator - 1]);
36229         
36230         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36231         
36232         this.prevIndicator.show();
36233         
36234         if(this.indicator == 1){
36235             this.prevIndicator.hide();
36236         }
36237         
36238         this.nextIndicator.show();
36239         
36240         if(this.indicator == this.files.length){
36241             this.nextIndicator.hide();
36242         }
36243         
36244         this.thumbEl.scrollTo('top');
36245         
36246         this.fireEvent('update', this);
36247     },
36248     
36249     onClick : function(e)
36250     {
36251         e.preventDefault();
36252         
36253         this.fireEvent('click', this);
36254     },
36255     
36256     prev : function(e)
36257     {
36258         e.preventDefault();
36259         
36260         this.indicator = Math.max(1, this.indicator - 1);
36261         
36262         this.update();
36263     },
36264     
36265     next : function(e)
36266     {
36267         e.preventDefault();
36268         
36269         this.indicator = Math.min(this.files.length, this.indicator + 1);
36270         
36271         this.update();
36272     }
36273 });
36274 /*
36275  * - LGPL
36276  *
36277  * RadioSet
36278  *
36279  *
36280  */
36281
36282 /**
36283  * @class Roo.bootstrap.RadioSet
36284  * @extends Roo.bootstrap.Input
36285  * Bootstrap RadioSet class
36286  * @cfg {String} indicatorpos (left|right) default left
36287  * @cfg {Boolean} inline (true|false) inline the element (default true)
36288  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36289  * @constructor
36290  * Create a new RadioSet
36291  * @param {Object} config The config object
36292  */
36293
36294 Roo.bootstrap.RadioSet = function(config){
36295     
36296     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36297     
36298     this.radioes = [];
36299     
36300     Roo.bootstrap.RadioSet.register(this);
36301     
36302     this.addEvents({
36303         /**
36304         * @event check
36305         * Fires when the element is checked or unchecked.
36306         * @param {Roo.bootstrap.RadioSet} this This radio
36307         * @param {Roo.bootstrap.Radio} item The checked item
36308         */
36309        check : true,
36310        /**
36311         * @event click
36312         * Fires when the element is click.
36313         * @param {Roo.bootstrap.RadioSet} this This radio set
36314         * @param {Roo.bootstrap.Radio} item The checked item
36315         * @param {Roo.EventObject} e The event object
36316         */
36317        click : true
36318     });
36319     
36320 };
36321
36322 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36323
36324     radioes : false,
36325     
36326     inline : true,
36327     
36328     weight : '',
36329     
36330     indicatorpos : 'left',
36331     
36332     getAutoCreate : function()
36333     {
36334         var label = {
36335             tag : 'label',
36336             cls : 'roo-radio-set-label',
36337             cn : [
36338                 {
36339                     tag : 'span',
36340                     html : this.fieldLabel
36341                 }
36342             ]
36343         };
36344         if (Roo.bootstrap.version == 3) {
36345             
36346             
36347             if(this.indicatorpos == 'left'){
36348                 label.cn.unshift({
36349                     tag : 'i',
36350                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36351                     tooltip : 'This field is required'
36352                 });
36353             } else {
36354                 label.cn.push({
36355                     tag : 'i',
36356                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36357                     tooltip : 'This field is required'
36358                 });
36359             }
36360         }
36361         var items = {
36362             tag : 'div',
36363             cls : 'roo-radio-set-items'
36364         };
36365         
36366         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36367         
36368         if (align === 'left' && this.fieldLabel.length) {
36369             
36370             items = {
36371                 cls : "roo-radio-set-right", 
36372                 cn: [
36373                     items
36374                 ]
36375             };
36376             
36377             if(this.labelWidth > 12){
36378                 label.style = "width: " + this.labelWidth + 'px';
36379             }
36380             
36381             if(this.labelWidth < 13 && this.labelmd == 0){
36382                 this.labelmd = this.labelWidth;
36383             }
36384             
36385             if(this.labellg > 0){
36386                 label.cls += ' col-lg-' + this.labellg;
36387                 items.cls += ' col-lg-' + (12 - this.labellg);
36388             }
36389             
36390             if(this.labelmd > 0){
36391                 label.cls += ' col-md-' + this.labelmd;
36392                 items.cls += ' col-md-' + (12 - this.labelmd);
36393             }
36394             
36395             if(this.labelsm > 0){
36396                 label.cls += ' col-sm-' + this.labelsm;
36397                 items.cls += ' col-sm-' + (12 - this.labelsm);
36398             }
36399             
36400             if(this.labelxs > 0){
36401                 label.cls += ' col-xs-' + this.labelxs;
36402                 items.cls += ' col-xs-' + (12 - this.labelxs);
36403             }
36404         }
36405         
36406         var cfg = {
36407             tag : 'div',
36408             cls : 'roo-radio-set',
36409             cn : [
36410                 {
36411                     tag : 'input',
36412                     cls : 'roo-radio-set-input',
36413                     type : 'hidden',
36414                     name : this.name,
36415                     value : this.value ? this.value :  ''
36416                 },
36417                 label,
36418                 items
36419             ]
36420         };
36421         
36422         if(this.weight.length){
36423             cfg.cls += ' roo-radio-' + this.weight;
36424         }
36425         
36426         if(this.inline) {
36427             cfg.cls += ' roo-radio-set-inline';
36428         }
36429         
36430         var settings=this;
36431         ['xs','sm','md','lg'].map(function(size){
36432             if (settings[size]) {
36433                 cfg.cls += ' col-' + size + '-' + settings[size];
36434             }
36435         });
36436         
36437         return cfg;
36438         
36439     },
36440
36441     initEvents : function()
36442     {
36443         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36444         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36445         
36446         if(!this.fieldLabel.length){
36447             this.labelEl.hide();
36448         }
36449         
36450         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36451         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36452         
36453         this.indicator = this.indicatorEl();
36454         
36455         if(this.indicator){
36456             this.indicator.addClass('invisible');
36457         }
36458         
36459         this.originalValue = this.getValue();
36460         
36461     },
36462     
36463     inputEl: function ()
36464     {
36465         return this.el.select('.roo-radio-set-input', true).first();
36466     },
36467     
36468     getChildContainer : function()
36469     {
36470         return this.itemsEl;
36471     },
36472     
36473     register : function(item)
36474     {
36475         this.radioes.push(item);
36476         
36477     },
36478     
36479     validate : function()
36480     {   
36481         if(this.getVisibilityEl().hasClass('hidden')){
36482             return true;
36483         }
36484         
36485         var valid = false;
36486         
36487         Roo.each(this.radioes, function(i){
36488             if(!i.checked){
36489                 return;
36490             }
36491             
36492             valid = true;
36493             return false;
36494         });
36495         
36496         if(this.allowBlank) {
36497             return true;
36498         }
36499         
36500         if(this.disabled || valid){
36501             this.markValid();
36502             return true;
36503         }
36504         
36505         this.markInvalid();
36506         return false;
36507         
36508     },
36509     
36510     markValid : function()
36511     {
36512         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36513             this.indicatorEl().removeClass('visible');
36514             this.indicatorEl().addClass('invisible');
36515         }
36516         
36517         
36518         if (Roo.bootstrap.version == 3) {
36519             this.el.removeClass([this.invalidClass, this.validClass]);
36520             this.el.addClass(this.validClass);
36521         } else {
36522             this.el.removeClass(['is-invalid','is-valid']);
36523             this.el.addClass(['is-valid']);
36524         }
36525         this.fireEvent('valid', this);
36526     },
36527     
36528     markInvalid : function(msg)
36529     {
36530         if(this.allowBlank || this.disabled){
36531             return;
36532         }
36533         
36534         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36535             this.indicatorEl().removeClass('invisible');
36536             this.indicatorEl().addClass('visible');
36537         }
36538         if (Roo.bootstrap.version == 3) {
36539             this.el.removeClass([this.invalidClass, this.validClass]);
36540             this.el.addClass(this.invalidClass);
36541         } else {
36542             this.el.removeClass(['is-invalid','is-valid']);
36543             this.el.addClass(['is-invalid']);
36544         }
36545         
36546         this.fireEvent('invalid', this, msg);
36547         
36548     },
36549     
36550     setValue : function(v, suppressEvent)
36551     {   
36552         if(this.value === v){
36553             return;
36554         }
36555         
36556         this.value = v;
36557         
36558         if(this.rendered){
36559             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36560         }
36561         
36562         Roo.each(this.radioes, function(i){
36563             i.checked = false;
36564             i.el.removeClass('checked');
36565         });
36566         
36567         Roo.each(this.radioes, function(i){
36568             
36569             if(i.value === v || i.value.toString() === v.toString()){
36570                 i.checked = true;
36571                 i.el.addClass('checked');
36572                 
36573                 if(suppressEvent !== true){
36574                     this.fireEvent('check', this, i);
36575                 }
36576                 
36577                 return false;
36578             }
36579             
36580         }, this);
36581         
36582         this.validate();
36583     },
36584     
36585     clearInvalid : function(){
36586         
36587         if(!this.el || this.preventMark){
36588             return;
36589         }
36590         
36591         this.el.removeClass([this.invalidClass]);
36592         
36593         this.fireEvent('valid', this);
36594     }
36595     
36596 });
36597
36598 Roo.apply(Roo.bootstrap.RadioSet, {
36599     
36600     groups: {},
36601     
36602     register : function(set)
36603     {
36604         this.groups[set.name] = set;
36605     },
36606     
36607     get: function(name) 
36608     {
36609         if (typeof(this.groups[name]) == 'undefined') {
36610             return false;
36611         }
36612         
36613         return this.groups[name] ;
36614     }
36615     
36616 });
36617 /*
36618  * Based on:
36619  * Ext JS Library 1.1.1
36620  * Copyright(c) 2006-2007, Ext JS, LLC.
36621  *
36622  * Originally Released Under LGPL - original licence link has changed is not relivant.
36623  *
36624  * Fork - LGPL
36625  * <script type="text/javascript">
36626  */
36627
36628
36629 /**
36630  * @class Roo.bootstrap.SplitBar
36631  * @extends Roo.util.Observable
36632  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36633  * <br><br>
36634  * Usage:
36635  * <pre><code>
36636 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36637                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36638 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36639 split.minSize = 100;
36640 split.maxSize = 600;
36641 split.animate = true;
36642 split.on('moved', splitterMoved);
36643 </code></pre>
36644  * @constructor
36645  * Create a new SplitBar
36646  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36647  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36648  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36649  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36650                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36651                         position of the SplitBar).
36652  */
36653 Roo.bootstrap.SplitBar = function(cfg){
36654     
36655     /** @private */
36656     
36657     //{
36658     //  dragElement : elm
36659     //  resizingElement: el,
36660         // optional..
36661     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36662     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36663         // existingProxy ???
36664     //}
36665     
36666     this.el = Roo.get(cfg.dragElement, true);
36667     this.el.dom.unselectable = "on";
36668     /** @private */
36669     this.resizingEl = Roo.get(cfg.resizingElement, true);
36670
36671     /**
36672      * @private
36673      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36674      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36675      * @type Number
36676      */
36677     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36678     
36679     /**
36680      * The minimum size of the resizing element. (Defaults to 0)
36681      * @type Number
36682      */
36683     this.minSize = 0;
36684     
36685     /**
36686      * The maximum size of the resizing element. (Defaults to 2000)
36687      * @type Number
36688      */
36689     this.maxSize = 2000;
36690     
36691     /**
36692      * Whether to animate the transition to the new size
36693      * @type Boolean
36694      */
36695     this.animate = false;
36696     
36697     /**
36698      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36699      * @type Boolean
36700      */
36701     this.useShim = false;
36702     
36703     /** @private */
36704     this.shim = null;
36705     
36706     if(!cfg.existingProxy){
36707         /** @private */
36708         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36709     }else{
36710         this.proxy = Roo.get(cfg.existingProxy).dom;
36711     }
36712     /** @private */
36713     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36714     
36715     /** @private */
36716     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36717     
36718     /** @private */
36719     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36720     
36721     /** @private */
36722     this.dragSpecs = {};
36723     
36724     /**
36725      * @private The adapter to use to positon and resize elements
36726      */
36727     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36728     this.adapter.init(this);
36729     
36730     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36731         /** @private */
36732         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36733         this.el.addClass("roo-splitbar-h");
36734     }else{
36735         /** @private */
36736         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36737         this.el.addClass("roo-splitbar-v");
36738     }
36739     
36740     this.addEvents({
36741         /**
36742          * @event resize
36743          * Fires when the splitter is moved (alias for {@link #event-moved})
36744          * @param {Roo.bootstrap.SplitBar} this
36745          * @param {Number} newSize the new width or height
36746          */
36747         "resize" : true,
36748         /**
36749          * @event moved
36750          * Fires when the splitter is moved
36751          * @param {Roo.bootstrap.SplitBar} this
36752          * @param {Number} newSize the new width or height
36753          */
36754         "moved" : true,
36755         /**
36756          * @event beforeresize
36757          * Fires before the splitter is dragged
36758          * @param {Roo.bootstrap.SplitBar} this
36759          */
36760         "beforeresize" : true,
36761
36762         "beforeapply" : true
36763     });
36764
36765     Roo.util.Observable.call(this);
36766 };
36767
36768 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36769     onStartProxyDrag : function(x, y){
36770         this.fireEvent("beforeresize", this);
36771         if(!this.overlay){
36772             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36773             o.unselectable();
36774             o.enableDisplayMode("block");
36775             // all splitbars share the same overlay
36776             Roo.bootstrap.SplitBar.prototype.overlay = o;
36777         }
36778         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36779         this.overlay.show();
36780         Roo.get(this.proxy).setDisplayed("block");
36781         var size = this.adapter.getElementSize(this);
36782         this.activeMinSize = this.getMinimumSize();;
36783         this.activeMaxSize = this.getMaximumSize();;
36784         var c1 = size - this.activeMinSize;
36785         var c2 = Math.max(this.activeMaxSize - size, 0);
36786         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36787             this.dd.resetConstraints();
36788             this.dd.setXConstraint(
36789                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36790                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36791             );
36792             this.dd.setYConstraint(0, 0);
36793         }else{
36794             this.dd.resetConstraints();
36795             this.dd.setXConstraint(0, 0);
36796             this.dd.setYConstraint(
36797                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36798                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36799             );
36800          }
36801         this.dragSpecs.startSize = size;
36802         this.dragSpecs.startPoint = [x, y];
36803         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36804     },
36805     
36806     /** 
36807      * @private Called after the drag operation by the DDProxy
36808      */
36809     onEndProxyDrag : function(e){
36810         Roo.get(this.proxy).setDisplayed(false);
36811         var endPoint = Roo.lib.Event.getXY(e);
36812         if(this.overlay){
36813             this.overlay.hide();
36814         }
36815         var newSize;
36816         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36817             newSize = this.dragSpecs.startSize + 
36818                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36819                     endPoint[0] - this.dragSpecs.startPoint[0] :
36820                     this.dragSpecs.startPoint[0] - endPoint[0]
36821                 );
36822         }else{
36823             newSize = this.dragSpecs.startSize + 
36824                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36825                     endPoint[1] - this.dragSpecs.startPoint[1] :
36826                     this.dragSpecs.startPoint[1] - endPoint[1]
36827                 );
36828         }
36829         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36830         if(newSize != this.dragSpecs.startSize){
36831             if(this.fireEvent('beforeapply', this, newSize) !== false){
36832                 this.adapter.setElementSize(this, newSize);
36833                 this.fireEvent("moved", this, newSize);
36834                 this.fireEvent("resize", this, newSize);
36835             }
36836         }
36837     },
36838     
36839     /**
36840      * Get the adapter this SplitBar uses
36841      * @return The adapter object
36842      */
36843     getAdapter : function(){
36844         return this.adapter;
36845     },
36846     
36847     /**
36848      * Set the adapter this SplitBar uses
36849      * @param {Object} adapter A SplitBar adapter object
36850      */
36851     setAdapter : function(adapter){
36852         this.adapter = adapter;
36853         this.adapter.init(this);
36854     },
36855     
36856     /**
36857      * Gets the minimum size for the resizing element
36858      * @return {Number} The minimum size
36859      */
36860     getMinimumSize : function(){
36861         return this.minSize;
36862     },
36863     
36864     /**
36865      * Sets the minimum size for the resizing element
36866      * @param {Number} minSize The minimum size
36867      */
36868     setMinimumSize : function(minSize){
36869         this.minSize = minSize;
36870     },
36871     
36872     /**
36873      * Gets the maximum size for the resizing element
36874      * @return {Number} The maximum size
36875      */
36876     getMaximumSize : function(){
36877         return this.maxSize;
36878     },
36879     
36880     /**
36881      * Sets the maximum size for the resizing element
36882      * @param {Number} maxSize The maximum size
36883      */
36884     setMaximumSize : function(maxSize){
36885         this.maxSize = maxSize;
36886     },
36887     
36888     /**
36889      * Sets the initialize size for the resizing element
36890      * @param {Number} size The initial size
36891      */
36892     setCurrentSize : function(size){
36893         var oldAnimate = this.animate;
36894         this.animate = false;
36895         this.adapter.setElementSize(this, size);
36896         this.animate = oldAnimate;
36897     },
36898     
36899     /**
36900      * Destroy this splitbar. 
36901      * @param {Boolean} removeEl True to remove the element
36902      */
36903     destroy : function(removeEl){
36904         if(this.shim){
36905             this.shim.remove();
36906         }
36907         this.dd.unreg();
36908         this.proxy.parentNode.removeChild(this.proxy);
36909         if(removeEl){
36910             this.el.remove();
36911         }
36912     }
36913 });
36914
36915 /**
36916  * @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.
36917  */
36918 Roo.bootstrap.SplitBar.createProxy = function(dir){
36919     var proxy = new Roo.Element(document.createElement("div"));
36920     proxy.unselectable();
36921     var cls = 'roo-splitbar-proxy';
36922     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36923     document.body.appendChild(proxy.dom);
36924     return proxy.dom;
36925 };
36926
36927 /** 
36928  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36929  * Default Adapter. It assumes the splitter and resizing element are not positioned
36930  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36931  */
36932 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36933 };
36934
36935 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36936     // do nothing for now
36937     init : function(s){
36938     
36939     },
36940     /**
36941      * Called before drag operations to get the current size of the resizing element. 
36942      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36943      */
36944      getElementSize : function(s){
36945         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36946             return s.resizingEl.getWidth();
36947         }else{
36948             return s.resizingEl.getHeight();
36949         }
36950     },
36951     
36952     /**
36953      * Called after drag operations to set the size of the resizing element.
36954      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36955      * @param {Number} newSize The new size to set
36956      * @param {Function} onComplete A function to be invoked when resizing is complete
36957      */
36958     setElementSize : function(s, newSize, onComplete){
36959         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36960             if(!s.animate){
36961                 s.resizingEl.setWidth(newSize);
36962                 if(onComplete){
36963                     onComplete(s, newSize);
36964                 }
36965             }else{
36966                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36967             }
36968         }else{
36969             
36970             if(!s.animate){
36971                 s.resizingEl.setHeight(newSize);
36972                 if(onComplete){
36973                     onComplete(s, newSize);
36974                 }
36975             }else{
36976                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36977             }
36978         }
36979     }
36980 };
36981
36982 /** 
36983  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36984  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36985  * Adapter that  moves the splitter element to align with the resized sizing element. 
36986  * Used with an absolute positioned SplitBar.
36987  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36988  * document.body, make sure you assign an id to the body element.
36989  */
36990 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36991     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36992     this.container = Roo.get(container);
36993 };
36994
36995 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36996     init : function(s){
36997         this.basic.init(s);
36998     },
36999     
37000     getElementSize : function(s){
37001         return this.basic.getElementSize(s);
37002     },
37003     
37004     setElementSize : function(s, newSize, onComplete){
37005         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37006     },
37007     
37008     moveSplitter : function(s){
37009         var yes = Roo.bootstrap.SplitBar;
37010         switch(s.placement){
37011             case yes.LEFT:
37012                 s.el.setX(s.resizingEl.getRight());
37013                 break;
37014             case yes.RIGHT:
37015                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37016                 break;
37017             case yes.TOP:
37018                 s.el.setY(s.resizingEl.getBottom());
37019                 break;
37020             case yes.BOTTOM:
37021                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37022                 break;
37023         }
37024     }
37025 };
37026
37027 /**
37028  * Orientation constant - Create a vertical SplitBar
37029  * @static
37030  * @type Number
37031  */
37032 Roo.bootstrap.SplitBar.VERTICAL = 1;
37033
37034 /**
37035  * Orientation constant - Create a horizontal SplitBar
37036  * @static
37037  * @type Number
37038  */
37039 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37040
37041 /**
37042  * Placement constant - The resizing element is to the left of the splitter element
37043  * @static
37044  * @type Number
37045  */
37046 Roo.bootstrap.SplitBar.LEFT = 1;
37047
37048 /**
37049  * Placement constant - The resizing element is to the right of the splitter element
37050  * @static
37051  * @type Number
37052  */
37053 Roo.bootstrap.SplitBar.RIGHT = 2;
37054
37055 /**
37056  * Placement constant - The resizing element is positioned above the splitter element
37057  * @static
37058  * @type Number
37059  */
37060 Roo.bootstrap.SplitBar.TOP = 3;
37061
37062 /**
37063  * Placement constant - The resizing element is positioned under splitter element
37064  * @static
37065  * @type Number
37066  */
37067 Roo.bootstrap.SplitBar.BOTTOM = 4;
37068 Roo.namespace("Roo.bootstrap.layout");/*
37069  * Based on:
37070  * Ext JS Library 1.1.1
37071  * Copyright(c) 2006-2007, Ext JS, LLC.
37072  *
37073  * Originally Released Under LGPL - original licence link has changed is not relivant.
37074  *
37075  * Fork - LGPL
37076  * <script type="text/javascript">
37077  */
37078
37079 /**
37080  * @class Roo.bootstrap.layout.Manager
37081  * @extends Roo.bootstrap.Component
37082  * Base class for layout managers.
37083  */
37084 Roo.bootstrap.layout.Manager = function(config)
37085 {
37086     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37087
37088
37089
37090
37091
37092     /** false to disable window resize monitoring @type Boolean */
37093     this.monitorWindowResize = true;
37094     this.regions = {};
37095     this.addEvents({
37096         /**
37097          * @event layout
37098          * Fires when a layout is performed.
37099          * @param {Roo.LayoutManager} this
37100          */
37101         "layout" : true,
37102         /**
37103          * @event regionresized
37104          * Fires when the user resizes a region.
37105          * @param {Roo.LayoutRegion} region The resized region
37106          * @param {Number} newSize The new size (width for east/west, height for north/south)
37107          */
37108         "regionresized" : true,
37109         /**
37110          * @event regioncollapsed
37111          * Fires when a region is collapsed.
37112          * @param {Roo.LayoutRegion} region The collapsed region
37113          */
37114         "regioncollapsed" : true,
37115         /**
37116          * @event regionexpanded
37117          * Fires when a region is expanded.
37118          * @param {Roo.LayoutRegion} region The expanded region
37119          */
37120         "regionexpanded" : true
37121     });
37122     this.updating = false;
37123
37124     if (config.el) {
37125         this.el = Roo.get(config.el);
37126         this.initEvents();
37127     }
37128
37129 };
37130
37131 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37132
37133
37134     regions : null,
37135
37136     monitorWindowResize : true,
37137
37138
37139     updating : false,
37140
37141
37142     onRender : function(ct, position)
37143     {
37144         if(!this.el){
37145             this.el = Roo.get(ct);
37146             this.initEvents();
37147         }
37148         //this.fireEvent('render',this);
37149     },
37150
37151
37152     initEvents: function()
37153     {
37154
37155
37156         // ie scrollbar fix
37157         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37158             document.body.scroll = "no";
37159         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37160             this.el.position('relative');
37161         }
37162         this.id = this.el.id;
37163         this.el.addClass("roo-layout-container");
37164         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37165         if(this.el.dom != document.body ) {
37166             this.el.on('resize', this.layout,this);
37167             this.el.on('show', this.layout,this);
37168         }
37169
37170     },
37171
37172     /**
37173      * Returns true if this layout is currently being updated
37174      * @return {Boolean}
37175      */
37176     isUpdating : function(){
37177         return this.updating;
37178     },
37179
37180     /**
37181      * Suspend the LayoutManager from doing auto-layouts while
37182      * making multiple add or remove calls
37183      */
37184     beginUpdate : function(){
37185         this.updating = true;
37186     },
37187
37188     /**
37189      * Restore auto-layouts and optionally disable the manager from performing a layout
37190      * @param {Boolean} noLayout true to disable a layout update
37191      */
37192     endUpdate : function(noLayout){
37193         this.updating = false;
37194         if(!noLayout){
37195             this.layout();
37196         }
37197     },
37198
37199     layout: function(){
37200         // abstract...
37201     },
37202
37203     onRegionResized : function(region, newSize){
37204         this.fireEvent("regionresized", region, newSize);
37205         this.layout();
37206     },
37207
37208     onRegionCollapsed : function(region){
37209         this.fireEvent("regioncollapsed", region);
37210     },
37211
37212     onRegionExpanded : function(region){
37213         this.fireEvent("regionexpanded", region);
37214     },
37215
37216     /**
37217      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37218      * performs box-model adjustments.
37219      * @return {Object} The size as an object {width: (the width), height: (the height)}
37220      */
37221     getViewSize : function()
37222     {
37223         var size;
37224         if(this.el.dom != document.body){
37225             size = this.el.getSize();
37226         }else{
37227             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37228         }
37229         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37230         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37231         return size;
37232     },
37233
37234     /**
37235      * Returns the Element this layout is bound to.
37236      * @return {Roo.Element}
37237      */
37238     getEl : function(){
37239         return this.el;
37240     },
37241
37242     /**
37243      * Returns the specified region.
37244      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37245      * @return {Roo.LayoutRegion}
37246      */
37247     getRegion : function(target){
37248         return this.regions[target.toLowerCase()];
37249     },
37250
37251     onWindowResize : function(){
37252         if(this.monitorWindowResize){
37253             this.layout();
37254         }
37255     }
37256 });
37257 /*
37258  * Based on:
37259  * Ext JS Library 1.1.1
37260  * Copyright(c) 2006-2007, Ext JS, LLC.
37261  *
37262  * Originally Released Under LGPL - original licence link has changed is not relivant.
37263  *
37264  * Fork - LGPL
37265  * <script type="text/javascript">
37266  */
37267 /**
37268  * @class Roo.bootstrap.layout.Border
37269  * @extends Roo.bootstrap.layout.Manager
37270  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37271  * please see: examples/bootstrap/nested.html<br><br>
37272  
37273 <b>The container the layout is rendered into can be either the body element or any other element.
37274 If it is not the body element, the container needs to either be an absolute positioned element,
37275 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37276 the container size if it is not the body element.</b>
37277
37278 * @constructor
37279 * Create a new Border
37280 * @param {Object} config Configuration options
37281  */
37282 Roo.bootstrap.layout.Border = function(config){
37283     config = config || {};
37284     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37285     
37286     
37287     
37288     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37289         if(config[region]){
37290             config[region].region = region;
37291             this.addRegion(config[region]);
37292         }
37293     },this);
37294     
37295 };
37296
37297 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37298
37299 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37300     
37301     parent : false, // this might point to a 'nest' or a ???
37302     
37303     /**
37304      * Creates and adds a new region if it doesn't already exist.
37305      * @param {String} target The target region key (north, south, east, west or center).
37306      * @param {Object} config The regions config object
37307      * @return {BorderLayoutRegion} The new region
37308      */
37309     addRegion : function(config)
37310     {
37311         if(!this.regions[config.region]){
37312             var r = this.factory(config);
37313             this.bindRegion(r);
37314         }
37315         return this.regions[config.region];
37316     },
37317
37318     // private (kinda)
37319     bindRegion : function(r){
37320         this.regions[r.config.region] = r;
37321         
37322         r.on("visibilitychange",    this.layout, this);
37323         r.on("paneladded",          this.layout, this);
37324         r.on("panelremoved",        this.layout, this);
37325         r.on("invalidated",         this.layout, this);
37326         r.on("resized",             this.onRegionResized, this);
37327         r.on("collapsed",           this.onRegionCollapsed, this);
37328         r.on("expanded",            this.onRegionExpanded, this);
37329     },
37330
37331     /**
37332      * Performs a layout update.
37333      */
37334     layout : function()
37335     {
37336         if(this.updating) {
37337             return;
37338         }
37339         
37340         // render all the rebions if they have not been done alreayd?
37341         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37342             if(this.regions[region] && !this.regions[region].bodyEl){
37343                 this.regions[region].onRender(this.el)
37344             }
37345         },this);
37346         
37347         var size = this.getViewSize();
37348         var w = size.width;
37349         var h = size.height;
37350         var centerW = w;
37351         var centerH = h;
37352         var centerY = 0;
37353         var centerX = 0;
37354         //var x = 0, y = 0;
37355
37356         var rs = this.regions;
37357         var north = rs["north"];
37358         var south = rs["south"]; 
37359         var west = rs["west"];
37360         var east = rs["east"];
37361         var center = rs["center"];
37362         //if(this.hideOnLayout){ // not supported anymore
37363             //c.el.setStyle("display", "none");
37364         //}
37365         if(north && north.isVisible()){
37366             var b = north.getBox();
37367             var m = north.getMargins();
37368             b.width = w - (m.left+m.right);
37369             b.x = m.left;
37370             b.y = m.top;
37371             centerY = b.height + b.y + m.bottom;
37372             centerH -= centerY;
37373             north.updateBox(this.safeBox(b));
37374         }
37375         if(south && south.isVisible()){
37376             var b = south.getBox();
37377             var m = south.getMargins();
37378             b.width = w - (m.left+m.right);
37379             b.x = m.left;
37380             var totalHeight = (b.height + m.top + m.bottom);
37381             b.y = h - totalHeight + m.top;
37382             centerH -= totalHeight;
37383             south.updateBox(this.safeBox(b));
37384         }
37385         if(west && west.isVisible()){
37386             var b = west.getBox();
37387             var m = west.getMargins();
37388             b.height = centerH - (m.top+m.bottom);
37389             b.x = m.left;
37390             b.y = centerY + m.top;
37391             var totalWidth = (b.width + m.left + m.right);
37392             centerX += totalWidth;
37393             centerW -= totalWidth;
37394             west.updateBox(this.safeBox(b));
37395         }
37396         if(east && east.isVisible()){
37397             var b = east.getBox();
37398             var m = east.getMargins();
37399             b.height = centerH - (m.top+m.bottom);
37400             var totalWidth = (b.width + m.left + m.right);
37401             b.x = w - totalWidth + m.left;
37402             b.y = centerY + m.top;
37403             centerW -= totalWidth;
37404             east.updateBox(this.safeBox(b));
37405         }
37406         if(center){
37407             var m = center.getMargins();
37408             var centerBox = {
37409                 x: centerX + m.left,
37410                 y: centerY + m.top,
37411                 width: centerW - (m.left+m.right),
37412                 height: centerH - (m.top+m.bottom)
37413             };
37414             //if(this.hideOnLayout){
37415                 //center.el.setStyle("display", "block");
37416             //}
37417             center.updateBox(this.safeBox(centerBox));
37418         }
37419         this.el.repaint();
37420         this.fireEvent("layout", this);
37421     },
37422
37423     // private
37424     safeBox : function(box){
37425         box.width = Math.max(0, box.width);
37426         box.height = Math.max(0, box.height);
37427         return box;
37428     },
37429
37430     /**
37431      * Adds a ContentPanel (or subclass) to this layout.
37432      * @param {String} target The target region key (north, south, east, west or center).
37433      * @param {Roo.ContentPanel} panel The panel to add
37434      * @return {Roo.ContentPanel} The added panel
37435      */
37436     add : function(target, panel){
37437          
37438         target = target.toLowerCase();
37439         return this.regions[target].add(panel);
37440     },
37441
37442     /**
37443      * Remove a ContentPanel (or subclass) to this layout.
37444      * @param {String} target The target region key (north, south, east, west or center).
37445      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37446      * @return {Roo.ContentPanel} The removed panel
37447      */
37448     remove : function(target, panel){
37449         target = target.toLowerCase();
37450         return this.regions[target].remove(panel);
37451     },
37452
37453     /**
37454      * Searches all regions for a panel with the specified id
37455      * @param {String} panelId
37456      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37457      */
37458     findPanel : function(panelId){
37459         var rs = this.regions;
37460         for(var target in rs){
37461             if(typeof rs[target] != "function"){
37462                 var p = rs[target].getPanel(panelId);
37463                 if(p){
37464                     return p;
37465                 }
37466             }
37467         }
37468         return null;
37469     },
37470
37471     /**
37472      * Searches all regions for a panel with the specified id and activates (shows) it.
37473      * @param {String/ContentPanel} panelId The panels id or the panel itself
37474      * @return {Roo.ContentPanel} The shown panel or null
37475      */
37476     showPanel : function(panelId) {
37477       var rs = this.regions;
37478       for(var target in rs){
37479          var r = rs[target];
37480          if(typeof r != "function"){
37481             if(r.hasPanel(panelId)){
37482                return r.showPanel(panelId);
37483             }
37484          }
37485       }
37486       return null;
37487    },
37488
37489    /**
37490      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37491      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37492      */
37493    /*
37494     restoreState : function(provider){
37495         if(!provider){
37496             provider = Roo.state.Manager;
37497         }
37498         var sm = new Roo.LayoutStateManager();
37499         sm.init(this, provider);
37500     },
37501 */
37502  
37503  
37504     /**
37505      * Adds a xtype elements to the layout.
37506      * <pre><code>
37507
37508 layout.addxtype({
37509        xtype : 'ContentPanel',
37510        region: 'west',
37511        items: [ .... ]
37512    }
37513 );
37514
37515 layout.addxtype({
37516         xtype : 'NestedLayoutPanel',
37517         region: 'west',
37518         layout: {
37519            center: { },
37520            west: { }   
37521         },
37522         items : [ ... list of content panels or nested layout panels.. ]
37523    }
37524 );
37525 </code></pre>
37526      * @param {Object} cfg Xtype definition of item to add.
37527      */
37528     addxtype : function(cfg)
37529     {
37530         // basically accepts a pannel...
37531         // can accept a layout region..!?!?
37532         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37533         
37534         
37535         // theory?  children can only be panels??
37536         
37537         //if (!cfg.xtype.match(/Panel$/)) {
37538         //    return false;
37539         //}
37540         var ret = false;
37541         
37542         if (typeof(cfg.region) == 'undefined') {
37543             Roo.log("Failed to add Panel, region was not set");
37544             Roo.log(cfg);
37545             return false;
37546         }
37547         var region = cfg.region;
37548         delete cfg.region;
37549         
37550           
37551         var xitems = [];
37552         if (cfg.items) {
37553             xitems = cfg.items;
37554             delete cfg.items;
37555         }
37556         var nb = false;
37557         
37558         if ( region == 'center') {
37559             Roo.log("Center: " + cfg.title);
37560         }
37561         
37562         
37563         switch(cfg.xtype) 
37564         {
37565             case 'Content':  // ContentPanel (el, cfg)
37566             case 'Scroll':  // ContentPanel (el, cfg)
37567             case 'View': 
37568                 cfg.autoCreate = cfg.autoCreate || true;
37569                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37570                 //} else {
37571                 //    var el = this.el.createChild();
37572                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37573                 //}
37574                 
37575                 this.add(region, ret);
37576                 break;
37577             
37578             /*
37579             case 'TreePanel': // our new panel!
37580                 cfg.el = this.el.createChild();
37581                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37582                 this.add(region, ret);
37583                 break;
37584             */
37585             
37586             case 'Nest': 
37587                 // create a new Layout (which is  a Border Layout...
37588                 
37589                 var clayout = cfg.layout;
37590                 clayout.el  = this.el.createChild();
37591                 clayout.items   = clayout.items  || [];
37592                 
37593                 delete cfg.layout;
37594                 
37595                 // replace this exitems with the clayout ones..
37596                 xitems = clayout.items;
37597                  
37598                 // force background off if it's in center...
37599                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37600                     cfg.background = false;
37601                 }
37602                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37603                 
37604                 
37605                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37606                 //console.log('adding nested layout panel '  + cfg.toSource());
37607                 this.add(region, ret);
37608                 nb = {}; /// find first...
37609                 break;
37610             
37611             case 'Grid':
37612                 
37613                 // needs grid and region
37614                 
37615                 //var el = this.getRegion(region).el.createChild();
37616                 /*
37617                  *var el = this.el.createChild();
37618                 // create the grid first...
37619                 cfg.grid.container = el;
37620                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37621                 */
37622                 
37623                 if (region == 'center' && this.active ) {
37624                     cfg.background = false;
37625                 }
37626                 
37627                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37628                 
37629                 this.add(region, ret);
37630                 /*
37631                 if (cfg.background) {
37632                     // render grid on panel activation (if panel background)
37633                     ret.on('activate', function(gp) {
37634                         if (!gp.grid.rendered) {
37635                     //        gp.grid.render(el);
37636                         }
37637                     });
37638                 } else {
37639                   //  cfg.grid.render(el);
37640                 }
37641                 */
37642                 break;
37643            
37644            
37645             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37646                 // it was the old xcomponent building that caused this before.
37647                 // espeically if border is the top element in the tree.
37648                 ret = this;
37649                 break; 
37650                 
37651                     
37652                 
37653                 
37654                 
37655             default:
37656                 /*
37657                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37658                     
37659                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37660                     this.add(region, ret);
37661                 } else {
37662                 */
37663                     Roo.log(cfg);
37664                     throw "Can not add '" + cfg.xtype + "' to Border";
37665                     return null;
37666              
37667                                 
37668              
37669         }
37670         this.beginUpdate();
37671         // add children..
37672         var region = '';
37673         var abn = {};
37674         Roo.each(xitems, function(i)  {
37675             region = nb && i.region ? i.region : false;
37676             
37677             var add = ret.addxtype(i);
37678            
37679             if (region) {
37680                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37681                 if (!i.background) {
37682                     abn[region] = nb[region] ;
37683                 }
37684             }
37685             
37686         });
37687         this.endUpdate();
37688
37689         // make the last non-background panel active..
37690         //if (nb) { Roo.log(abn); }
37691         if (nb) {
37692             
37693             for(var r in abn) {
37694                 region = this.getRegion(r);
37695                 if (region) {
37696                     // tried using nb[r], but it does not work..
37697                      
37698                     region.showPanel(abn[r]);
37699                    
37700                 }
37701             }
37702         }
37703         return ret;
37704         
37705     },
37706     
37707     
37708 // private
37709     factory : function(cfg)
37710     {
37711         
37712         var validRegions = Roo.bootstrap.layout.Border.regions;
37713
37714         var target = cfg.region;
37715         cfg.mgr = this;
37716         
37717         var r = Roo.bootstrap.layout;
37718         Roo.log(target);
37719         switch(target){
37720             case "north":
37721                 return new r.North(cfg);
37722             case "south":
37723                 return new r.South(cfg);
37724             case "east":
37725                 return new r.East(cfg);
37726             case "west":
37727                 return new r.West(cfg);
37728             case "center":
37729                 return new r.Center(cfg);
37730         }
37731         throw 'Layout region "'+target+'" not supported.';
37732     }
37733     
37734     
37735 });
37736  /*
37737  * Based on:
37738  * Ext JS Library 1.1.1
37739  * Copyright(c) 2006-2007, Ext JS, LLC.
37740  *
37741  * Originally Released Under LGPL - original licence link has changed is not relivant.
37742  *
37743  * Fork - LGPL
37744  * <script type="text/javascript">
37745  */
37746  
37747 /**
37748  * @class Roo.bootstrap.layout.Basic
37749  * @extends Roo.util.Observable
37750  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37751  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37752  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37753  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37754  * @cfg {string}   region  the region that it inhabits..
37755  * @cfg {bool}   skipConfig skip config?
37756  * 
37757
37758  */
37759 Roo.bootstrap.layout.Basic = function(config){
37760     
37761     this.mgr = config.mgr;
37762     
37763     this.position = config.region;
37764     
37765     var skipConfig = config.skipConfig;
37766     
37767     this.events = {
37768         /**
37769          * @scope Roo.BasicLayoutRegion
37770          */
37771         
37772         /**
37773          * @event beforeremove
37774          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37775          * @param {Roo.LayoutRegion} this
37776          * @param {Roo.ContentPanel} panel The panel
37777          * @param {Object} e The cancel event object
37778          */
37779         "beforeremove" : true,
37780         /**
37781          * @event invalidated
37782          * Fires when the layout for this region is changed.
37783          * @param {Roo.LayoutRegion} this
37784          */
37785         "invalidated" : true,
37786         /**
37787          * @event visibilitychange
37788          * Fires when this region is shown or hidden 
37789          * @param {Roo.LayoutRegion} this
37790          * @param {Boolean} visibility true or false
37791          */
37792         "visibilitychange" : true,
37793         /**
37794          * @event paneladded
37795          * Fires when a panel is added. 
37796          * @param {Roo.LayoutRegion} this
37797          * @param {Roo.ContentPanel} panel The panel
37798          */
37799         "paneladded" : true,
37800         /**
37801          * @event panelremoved
37802          * Fires when a panel is removed. 
37803          * @param {Roo.LayoutRegion} this
37804          * @param {Roo.ContentPanel} panel The panel
37805          */
37806         "panelremoved" : true,
37807         /**
37808          * @event beforecollapse
37809          * Fires when this region before collapse.
37810          * @param {Roo.LayoutRegion} this
37811          */
37812         "beforecollapse" : true,
37813         /**
37814          * @event collapsed
37815          * Fires when this region is collapsed.
37816          * @param {Roo.LayoutRegion} this
37817          */
37818         "collapsed" : true,
37819         /**
37820          * @event expanded
37821          * Fires when this region is expanded.
37822          * @param {Roo.LayoutRegion} this
37823          */
37824         "expanded" : true,
37825         /**
37826          * @event slideshow
37827          * Fires when this region is slid into view.
37828          * @param {Roo.LayoutRegion} this
37829          */
37830         "slideshow" : true,
37831         /**
37832          * @event slidehide
37833          * Fires when this region slides out of view. 
37834          * @param {Roo.LayoutRegion} this
37835          */
37836         "slidehide" : true,
37837         /**
37838          * @event panelactivated
37839          * Fires when a panel is activated. 
37840          * @param {Roo.LayoutRegion} this
37841          * @param {Roo.ContentPanel} panel The activated panel
37842          */
37843         "panelactivated" : true,
37844         /**
37845          * @event resized
37846          * Fires when the user resizes this region. 
37847          * @param {Roo.LayoutRegion} this
37848          * @param {Number} newSize The new size (width for east/west, height for north/south)
37849          */
37850         "resized" : true
37851     };
37852     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37853     this.panels = new Roo.util.MixedCollection();
37854     this.panels.getKey = this.getPanelId.createDelegate(this);
37855     this.box = null;
37856     this.activePanel = null;
37857     // ensure listeners are added...
37858     
37859     if (config.listeners || config.events) {
37860         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37861             listeners : config.listeners || {},
37862             events : config.events || {}
37863         });
37864     }
37865     
37866     if(skipConfig !== true){
37867         this.applyConfig(config);
37868     }
37869 };
37870
37871 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37872 {
37873     getPanelId : function(p){
37874         return p.getId();
37875     },
37876     
37877     applyConfig : function(config){
37878         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37879         this.config = config;
37880         
37881     },
37882     
37883     /**
37884      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37885      * the width, for horizontal (north, south) the height.
37886      * @param {Number} newSize The new width or height
37887      */
37888     resizeTo : function(newSize){
37889         var el = this.el ? this.el :
37890                  (this.activePanel ? this.activePanel.getEl() : null);
37891         if(el){
37892             switch(this.position){
37893                 case "east":
37894                 case "west":
37895                     el.setWidth(newSize);
37896                     this.fireEvent("resized", this, newSize);
37897                 break;
37898                 case "north":
37899                 case "south":
37900                     el.setHeight(newSize);
37901                     this.fireEvent("resized", this, newSize);
37902                 break;                
37903             }
37904         }
37905     },
37906     
37907     getBox : function(){
37908         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37909     },
37910     
37911     getMargins : function(){
37912         return this.margins;
37913     },
37914     
37915     updateBox : function(box){
37916         this.box = box;
37917         var el = this.activePanel.getEl();
37918         el.dom.style.left = box.x + "px";
37919         el.dom.style.top = box.y + "px";
37920         this.activePanel.setSize(box.width, box.height);
37921     },
37922     
37923     /**
37924      * Returns the container element for this region.
37925      * @return {Roo.Element}
37926      */
37927     getEl : function(){
37928         return this.activePanel;
37929     },
37930     
37931     /**
37932      * Returns true if this region is currently visible.
37933      * @return {Boolean}
37934      */
37935     isVisible : function(){
37936         return this.activePanel ? true : false;
37937     },
37938     
37939     setActivePanel : function(panel){
37940         panel = this.getPanel(panel);
37941         if(this.activePanel && this.activePanel != panel){
37942             this.activePanel.setActiveState(false);
37943             this.activePanel.getEl().setLeftTop(-10000,-10000);
37944         }
37945         this.activePanel = panel;
37946         panel.setActiveState(true);
37947         if(this.box){
37948             panel.setSize(this.box.width, this.box.height);
37949         }
37950         this.fireEvent("panelactivated", this, panel);
37951         this.fireEvent("invalidated");
37952     },
37953     
37954     /**
37955      * Show the specified panel.
37956      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37957      * @return {Roo.ContentPanel} The shown panel or null
37958      */
37959     showPanel : function(panel){
37960         panel = this.getPanel(panel);
37961         if(panel){
37962             this.setActivePanel(panel);
37963         }
37964         return panel;
37965     },
37966     
37967     /**
37968      * Get the active panel for this region.
37969      * @return {Roo.ContentPanel} The active panel or null
37970      */
37971     getActivePanel : function(){
37972         return this.activePanel;
37973     },
37974     
37975     /**
37976      * Add the passed ContentPanel(s)
37977      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37978      * @return {Roo.ContentPanel} The panel added (if only one was added)
37979      */
37980     add : function(panel){
37981         if(arguments.length > 1){
37982             for(var i = 0, len = arguments.length; i < len; i++) {
37983                 this.add(arguments[i]);
37984             }
37985             return null;
37986         }
37987         if(this.hasPanel(panel)){
37988             this.showPanel(panel);
37989             return panel;
37990         }
37991         var el = panel.getEl();
37992         if(el.dom.parentNode != this.mgr.el.dom){
37993             this.mgr.el.dom.appendChild(el.dom);
37994         }
37995         if(panel.setRegion){
37996             panel.setRegion(this);
37997         }
37998         this.panels.add(panel);
37999         el.setStyle("position", "absolute");
38000         if(!panel.background){
38001             this.setActivePanel(panel);
38002             if(this.config.initialSize && this.panels.getCount()==1){
38003                 this.resizeTo(this.config.initialSize);
38004             }
38005         }
38006         this.fireEvent("paneladded", this, panel);
38007         return panel;
38008     },
38009     
38010     /**
38011      * Returns true if the panel is in this region.
38012      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38013      * @return {Boolean}
38014      */
38015     hasPanel : function(panel){
38016         if(typeof panel == "object"){ // must be panel obj
38017             panel = panel.getId();
38018         }
38019         return this.getPanel(panel) ? true : false;
38020     },
38021     
38022     /**
38023      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38024      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38025      * @param {Boolean} preservePanel Overrides the config preservePanel option
38026      * @return {Roo.ContentPanel} The panel that was removed
38027      */
38028     remove : function(panel, preservePanel){
38029         panel = this.getPanel(panel);
38030         if(!panel){
38031             return null;
38032         }
38033         var e = {};
38034         this.fireEvent("beforeremove", this, panel, e);
38035         if(e.cancel === true){
38036             return null;
38037         }
38038         var panelId = panel.getId();
38039         this.panels.removeKey(panelId);
38040         return panel;
38041     },
38042     
38043     /**
38044      * Returns the panel specified or null if it's not in this region.
38045      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38046      * @return {Roo.ContentPanel}
38047      */
38048     getPanel : function(id){
38049         if(typeof id == "object"){ // must be panel obj
38050             return id;
38051         }
38052         return this.panels.get(id);
38053     },
38054     
38055     /**
38056      * Returns this regions position (north/south/east/west/center).
38057      * @return {String} 
38058      */
38059     getPosition: function(){
38060         return this.position;    
38061     }
38062 });/*
38063  * Based on:
38064  * Ext JS Library 1.1.1
38065  * Copyright(c) 2006-2007, Ext JS, LLC.
38066  *
38067  * Originally Released Under LGPL - original licence link has changed is not relivant.
38068  *
38069  * Fork - LGPL
38070  * <script type="text/javascript">
38071  */
38072  
38073 /**
38074  * @class Roo.bootstrap.layout.Region
38075  * @extends Roo.bootstrap.layout.Basic
38076  * This class represents a region in a layout manager.
38077  
38078  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38079  * @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})
38080  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38081  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38082  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38083  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38084  * @cfg {String}    title           The title for the region (overrides panel titles)
38085  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38086  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38087  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38088  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38089  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38090  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38091  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38092  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38093  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38094  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38095
38096  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38097  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38098  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38099  * @cfg {Number}    width           For East/West panels
38100  * @cfg {Number}    height          For North/South panels
38101  * @cfg {Boolean}   split           To show the splitter
38102  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38103  * 
38104  * @cfg {string}   cls             Extra CSS classes to add to region
38105  * 
38106  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38107  * @cfg {string}   region  the region that it inhabits..
38108  *
38109
38110  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38111  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38112
38113  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38114  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38115  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38116  */
38117 Roo.bootstrap.layout.Region = function(config)
38118 {
38119     this.applyConfig(config);
38120
38121     var mgr = config.mgr;
38122     var pos = config.region;
38123     config.skipConfig = true;
38124     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38125     
38126     if (mgr.el) {
38127         this.onRender(mgr.el);   
38128     }
38129      
38130     this.visible = true;
38131     this.collapsed = false;
38132     this.unrendered_panels = [];
38133 };
38134
38135 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38136
38137     position: '', // set by wrapper (eg. north/south etc..)
38138     unrendered_panels : null,  // unrendered panels.
38139     
38140     tabPosition : false,
38141     
38142     mgr: false, // points to 'Border'
38143     
38144     
38145     createBody : function(){
38146         /** This region's body element 
38147         * @type Roo.Element */
38148         this.bodyEl = this.el.createChild({
38149                 tag: "div",
38150                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38151         });
38152     },
38153
38154     onRender: function(ctr, pos)
38155     {
38156         var dh = Roo.DomHelper;
38157         /** This region's container element 
38158         * @type Roo.Element */
38159         this.el = dh.append(ctr.dom, {
38160                 tag: "div",
38161                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38162             }, true);
38163         /** This region's title element 
38164         * @type Roo.Element */
38165     
38166         this.titleEl = dh.append(this.el.dom,  {
38167                 tag: "div",
38168                 unselectable: "on",
38169                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38170                 children:[
38171                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38172                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38173                 ]
38174             }, true);
38175         
38176         this.titleEl.enableDisplayMode();
38177         /** This region's title text element 
38178         * @type HTMLElement */
38179         this.titleTextEl = this.titleEl.dom.firstChild;
38180         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38181         /*
38182         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38183         this.closeBtn.enableDisplayMode();
38184         this.closeBtn.on("click", this.closeClicked, this);
38185         this.closeBtn.hide();
38186     */
38187         this.createBody(this.config);
38188         if(this.config.hideWhenEmpty){
38189             this.hide();
38190             this.on("paneladded", this.validateVisibility, this);
38191             this.on("panelremoved", this.validateVisibility, this);
38192         }
38193         if(this.autoScroll){
38194             this.bodyEl.setStyle("overflow", "auto");
38195         }else{
38196             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38197         }
38198         //if(c.titlebar !== false){
38199             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38200                 this.titleEl.hide();
38201             }else{
38202                 this.titleEl.show();
38203                 if(this.config.title){
38204                     this.titleTextEl.innerHTML = this.config.title;
38205                 }
38206             }
38207         //}
38208         if(this.config.collapsed){
38209             this.collapse(true);
38210         }
38211         if(this.config.hidden){
38212             this.hide();
38213         }
38214         
38215         if (this.unrendered_panels && this.unrendered_panels.length) {
38216             for (var i =0;i< this.unrendered_panels.length; i++) {
38217                 this.add(this.unrendered_panels[i]);
38218             }
38219             this.unrendered_panels = null;
38220             
38221         }
38222         
38223     },
38224     
38225     applyConfig : function(c)
38226     {
38227         /*
38228          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38229             var dh = Roo.DomHelper;
38230             if(c.titlebar !== false){
38231                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38232                 this.collapseBtn.on("click", this.collapse, this);
38233                 this.collapseBtn.enableDisplayMode();
38234                 /*
38235                 if(c.showPin === true || this.showPin){
38236                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38237                     this.stickBtn.enableDisplayMode();
38238                     this.stickBtn.on("click", this.expand, this);
38239                     this.stickBtn.hide();
38240                 }
38241                 
38242             }
38243             */
38244             /** This region's collapsed element
38245             * @type Roo.Element */
38246             /*
38247              *
38248             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38249                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38250             ]}, true);
38251             
38252             if(c.floatable !== false){
38253                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38254                this.collapsedEl.on("click", this.collapseClick, this);
38255             }
38256
38257             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38258                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38259                    id: "message", unselectable: "on", style:{"float":"left"}});
38260                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38261              }
38262             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38263             this.expandBtn.on("click", this.expand, this);
38264             
38265         }
38266         
38267         if(this.collapseBtn){
38268             this.collapseBtn.setVisible(c.collapsible == true);
38269         }
38270         
38271         this.cmargins = c.cmargins || this.cmargins ||
38272                          (this.position == "west" || this.position == "east" ?
38273                              {top: 0, left: 2, right:2, bottom: 0} :
38274                              {top: 2, left: 0, right:0, bottom: 2});
38275         */
38276         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38277         
38278         
38279         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38280         
38281         this.autoScroll = c.autoScroll || false;
38282         
38283         
38284        
38285         
38286         this.duration = c.duration || .30;
38287         this.slideDuration = c.slideDuration || .45;
38288         this.config = c;
38289        
38290     },
38291     /**
38292      * Returns true if this region is currently visible.
38293      * @return {Boolean}
38294      */
38295     isVisible : function(){
38296         return this.visible;
38297     },
38298
38299     /**
38300      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38301      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38302      */
38303     //setCollapsedTitle : function(title){
38304     //    title = title || "&#160;";
38305      //   if(this.collapsedTitleTextEl){
38306       //      this.collapsedTitleTextEl.innerHTML = title;
38307        // }
38308     //},
38309
38310     getBox : function(){
38311         var b;
38312       //  if(!this.collapsed){
38313             b = this.el.getBox(false, true);
38314        // }else{
38315           //  b = this.collapsedEl.getBox(false, true);
38316         //}
38317         return b;
38318     },
38319
38320     getMargins : function(){
38321         return this.margins;
38322         //return this.collapsed ? this.cmargins : this.margins;
38323     },
38324 /*
38325     highlight : function(){
38326         this.el.addClass("x-layout-panel-dragover");
38327     },
38328
38329     unhighlight : function(){
38330         this.el.removeClass("x-layout-panel-dragover");
38331     },
38332 */
38333     updateBox : function(box)
38334     {
38335         if (!this.bodyEl) {
38336             return; // not rendered yet..
38337         }
38338         
38339         this.box = box;
38340         if(!this.collapsed){
38341             this.el.dom.style.left = box.x + "px";
38342             this.el.dom.style.top = box.y + "px";
38343             this.updateBody(box.width, box.height);
38344         }else{
38345             this.collapsedEl.dom.style.left = box.x + "px";
38346             this.collapsedEl.dom.style.top = box.y + "px";
38347             this.collapsedEl.setSize(box.width, box.height);
38348         }
38349         if(this.tabs){
38350             this.tabs.autoSizeTabs();
38351         }
38352     },
38353
38354     updateBody : function(w, h)
38355     {
38356         if(w !== null){
38357             this.el.setWidth(w);
38358             w -= this.el.getBorderWidth("rl");
38359             if(this.config.adjustments){
38360                 w += this.config.adjustments[0];
38361             }
38362         }
38363         if(h !== null && h > 0){
38364             this.el.setHeight(h);
38365             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38366             h -= this.el.getBorderWidth("tb");
38367             if(this.config.adjustments){
38368                 h += this.config.adjustments[1];
38369             }
38370             this.bodyEl.setHeight(h);
38371             if(this.tabs){
38372                 h = this.tabs.syncHeight(h);
38373             }
38374         }
38375         if(this.panelSize){
38376             w = w !== null ? w : this.panelSize.width;
38377             h = h !== null ? h : this.panelSize.height;
38378         }
38379         if(this.activePanel){
38380             var el = this.activePanel.getEl();
38381             w = w !== null ? w : el.getWidth();
38382             h = h !== null ? h : el.getHeight();
38383             this.panelSize = {width: w, height: h};
38384             this.activePanel.setSize(w, h);
38385         }
38386         if(Roo.isIE && this.tabs){
38387             this.tabs.el.repaint();
38388         }
38389     },
38390
38391     /**
38392      * Returns the container element for this region.
38393      * @return {Roo.Element}
38394      */
38395     getEl : function(){
38396         return this.el;
38397     },
38398
38399     /**
38400      * Hides this region.
38401      */
38402     hide : function(){
38403         //if(!this.collapsed){
38404             this.el.dom.style.left = "-2000px";
38405             this.el.hide();
38406         //}else{
38407          //   this.collapsedEl.dom.style.left = "-2000px";
38408          //   this.collapsedEl.hide();
38409        // }
38410         this.visible = false;
38411         this.fireEvent("visibilitychange", this, false);
38412     },
38413
38414     /**
38415      * Shows this region if it was previously hidden.
38416      */
38417     show : function(){
38418         //if(!this.collapsed){
38419             this.el.show();
38420         //}else{
38421         //    this.collapsedEl.show();
38422        // }
38423         this.visible = true;
38424         this.fireEvent("visibilitychange", this, true);
38425     },
38426 /*
38427     closeClicked : function(){
38428         if(this.activePanel){
38429             this.remove(this.activePanel);
38430         }
38431     },
38432
38433     collapseClick : function(e){
38434         if(this.isSlid){
38435            e.stopPropagation();
38436            this.slideIn();
38437         }else{
38438            e.stopPropagation();
38439            this.slideOut();
38440         }
38441     },
38442 */
38443     /**
38444      * Collapses this region.
38445      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38446      */
38447     /*
38448     collapse : function(skipAnim, skipCheck = false){
38449         if(this.collapsed) {
38450             return;
38451         }
38452         
38453         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38454             
38455             this.collapsed = true;
38456             if(this.split){
38457                 this.split.el.hide();
38458             }
38459             if(this.config.animate && skipAnim !== true){
38460                 this.fireEvent("invalidated", this);
38461                 this.animateCollapse();
38462             }else{
38463                 this.el.setLocation(-20000,-20000);
38464                 this.el.hide();
38465                 this.collapsedEl.show();
38466                 this.fireEvent("collapsed", this);
38467                 this.fireEvent("invalidated", this);
38468             }
38469         }
38470         
38471     },
38472 */
38473     animateCollapse : function(){
38474         // overridden
38475     },
38476
38477     /**
38478      * Expands this region if it was previously collapsed.
38479      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38480      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38481      */
38482     /*
38483     expand : function(e, skipAnim){
38484         if(e) {
38485             e.stopPropagation();
38486         }
38487         if(!this.collapsed || this.el.hasActiveFx()) {
38488             return;
38489         }
38490         if(this.isSlid){
38491             this.afterSlideIn();
38492             skipAnim = true;
38493         }
38494         this.collapsed = false;
38495         if(this.config.animate && skipAnim !== true){
38496             this.animateExpand();
38497         }else{
38498             this.el.show();
38499             if(this.split){
38500                 this.split.el.show();
38501             }
38502             this.collapsedEl.setLocation(-2000,-2000);
38503             this.collapsedEl.hide();
38504             this.fireEvent("invalidated", this);
38505             this.fireEvent("expanded", this);
38506         }
38507     },
38508 */
38509     animateExpand : function(){
38510         // overridden
38511     },
38512
38513     initTabs : function()
38514     {
38515         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38516         
38517         var ts = new Roo.bootstrap.panel.Tabs({
38518             el: this.bodyEl.dom,
38519             region : this,
38520             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38521             disableTooltips: this.config.disableTabTips,
38522             toolbar : this.config.toolbar
38523         });
38524         
38525         if(this.config.hideTabs){
38526             ts.stripWrap.setDisplayed(false);
38527         }
38528         this.tabs = ts;
38529         ts.resizeTabs = this.config.resizeTabs === true;
38530         ts.minTabWidth = this.config.minTabWidth || 40;
38531         ts.maxTabWidth = this.config.maxTabWidth || 250;
38532         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38533         ts.monitorResize = false;
38534         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38535         ts.bodyEl.addClass('roo-layout-tabs-body');
38536         this.panels.each(this.initPanelAsTab, this);
38537     },
38538
38539     initPanelAsTab : function(panel){
38540         var ti = this.tabs.addTab(
38541             panel.getEl().id,
38542             panel.getTitle(),
38543             null,
38544             this.config.closeOnTab && panel.isClosable(),
38545             panel.tpl
38546         );
38547         if(panel.tabTip !== undefined){
38548             ti.setTooltip(panel.tabTip);
38549         }
38550         ti.on("activate", function(){
38551               this.setActivePanel(panel);
38552         }, this);
38553         
38554         if(this.config.closeOnTab){
38555             ti.on("beforeclose", function(t, e){
38556                 e.cancel = true;
38557                 this.remove(panel);
38558             }, this);
38559         }
38560         
38561         panel.tabItem = ti;
38562         
38563         return ti;
38564     },
38565
38566     updatePanelTitle : function(panel, title)
38567     {
38568         if(this.activePanel == panel){
38569             this.updateTitle(title);
38570         }
38571         if(this.tabs){
38572             var ti = this.tabs.getTab(panel.getEl().id);
38573             ti.setText(title);
38574             if(panel.tabTip !== undefined){
38575                 ti.setTooltip(panel.tabTip);
38576             }
38577         }
38578     },
38579
38580     updateTitle : function(title){
38581         if(this.titleTextEl && !this.config.title){
38582             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38583         }
38584     },
38585
38586     setActivePanel : function(panel)
38587     {
38588         panel = this.getPanel(panel);
38589         if(this.activePanel && this.activePanel != panel){
38590             if(this.activePanel.setActiveState(false) === false){
38591                 return;
38592             }
38593         }
38594         this.activePanel = panel;
38595         panel.setActiveState(true);
38596         if(this.panelSize){
38597             panel.setSize(this.panelSize.width, this.panelSize.height);
38598         }
38599         if(this.closeBtn){
38600             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38601         }
38602         this.updateTitle(panel.getTitle());
38603         if(this.tabs){
38604             this.fireEvent("invalidated", this);
38605         }
38606         this.fireEvent("panelactivated", this, panel);
38607     },
38608
38609     /**
38610      * Shows the specified panel.
38611      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38612      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38613      */
38614     showPanel : function(panel)
38615     {
38616         panel = this.getPanel(panel);
38617         if(panel){
38618             if(this.tabs){
38619                 var tab = this.tabs.getTab(panel.getEl().id);
38620                 if(tab.isHidden()){
38621                     this.tabs.unhideTab(tab.id);
38622                 }
38623                 tab.activate();
38624             }else{
38625                 this.setActivePanel(panel);
38626             }
38627         }
38628         return panel;
38629     },
38630
38631     /**
38632      * Get the active panel for this region.
38633      * @return {Roo.ContentPanel} The active panel or null
38634      */
38635     getActivePanel : function(){
38636         return this.activePanel;
38637     },
38638
38639     validateVisibility : function(){
38640         if(this.panels.getCount() < 1){
38641             this.updateTitle("&#160;");
38642             this.closeBtn.hide();
38643             this.hide();
38644         }else{
38645             if(!this.isVisible()){
38646                 this.show();
38647             }
38648         }
38649     },
38650
38651     /**
38652      * Adds the passed ContentPanel(s) to this region.
38653      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38654      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38655      */
38656     add : function(panel)
38657     {
38658         if(arguments.length > 1){
38659             for(var i = 0, len = arguments.length; i < len; i++) {
38660                 this.add(arguments[i]);
38661             }
38662             return null;
38663         }
38664         
38665         // if we have not been rendered yet, then we can not really do much of this..
38666         if (!this.bodyEl) {
38667             this.unrendered_panels.push(panel);
38668             return panel;
38669         }
38670         
38671         
38672         
38673         
38674         if(this.hasPanel(panel)){
38675             this.showPanel(panel);
38676             return panel;
38677         }
38678         panel.setRegion(this);
38679         this.panels.add(panel);
38680        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38681             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38682             // and hide them... ???
38683             this.bodyEl.dom.appendChild(panel.getEl().dom);
38684             if(panel.background !== true){
38685                 this.setActivePanel(panel);
38686             }
38687             this.fireEvent("paneladded", this, panel);
38688             return panel;
38689         }
38690         */
38691         if(!this.tabs){
38692             this.initTabs();
38693         }else{
38694             this.initPanelAsTab(panel);
38695         }
38696         
38697         
38698         if(panel.background !== true){
38699             this.tabs.activate(panel.getEl().id);
38700         }
38701         this.fireEvent("paneladded", this, panel);
38702         return panel;
38703     },
38704
38705     /**
38706      * Hides the tab for the specified panel.
38707      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38708      */
38709     hidePanel : function(panel){
38710         if(this.tabs && (panel = this.getPanel(panel))){
38711             this.tabs.hideTab(panel.getEl().id);
38712         }
38713     },
38714
38715     /**
38716      * Unhides the tab for a previously hidden panel.
38717      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38718      */
38719     unhidePanel : function(panel){
38720         if(this.tabs && (panel = this.getPanel(panel))){
38721             this.tabs.unhideTab(panel.getEl().id);
38722         }
38723     },
38724
38725     clearPanels : function(){
38726         while(this.panels.getCount() > 0){
38727              this.remove(this.panels.first());
38728         }
38729     },
38730
38731     /**
38732      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38733      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38734      * @param {Boolean} preservePanel Overrides the config preservePanel option
38735      * @return {Roo.ContentPanel} The panel that was removed
38736      */
38737     remove : function(panel, preservePanel)
38738     {
38739         panel = this.getPanel(panel);
38740         if(!panel){
38741             return null;
38742         }
38743         var e = {};
38744         this.fireEvent("beforeremove", this, panel, e);
38745         if(e.cancel === true){
38746             return null;
38747         }
38748         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38749         var panelId = panel.getId();
38750         this.panels.removeKey(panelId);
38751         if(preservePanel){
38752             document.body.appendChild(panel.getEl().dom);
38753         }
38754         if(this.tabs){
38755             this.tabs.removeTab(panel.getEl().id);
38756         }else if (!preservePanel){
38757             this.bodyEl.dom.removeChild(panel.getEl().dom);
38758         }
38759         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38760             var p = this.panels.first();
38761             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38762             tempEl.appendChild(p.getEl().dom);
38763             this.bodyEl.update("");
38764             this.bodyEl.dom.appendChild(p.getEl().dom);
38765             tempEl = null;
38766             this.updateTitle(p.getTitle());
38767             this.tabs = null;
38768             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38769             this.setActivePanel(p);
38770         }
38771         panel.setRegion(null);
38772         if(this.activePanel == panel){
38773             this.activePanel = null;
38774         }
38775         if(this.config.autoDestroy !== false && preservePanel !== true){
38776             try{panel.destroy();}catch(e){}
38777         }
38778         this.fireEvent("panelremoved", this, panel);
38779         return panel;
38780     },
38781
38782     /**
38783      * Returns the TabPanel component used by this region
38784      * @return {Roo.TabPanel}
38785      */
38786     getTabs : function(){
38787         return this.tabs;
38788     },
38789
38790     createTool : function(parentEl, className){
38791         var btn = Roo.DomHelper.append(parentEl, {
38792             tag: "div",
38793             cls: "x-layout-tools-button",
38794             children: [ {
38795                 tag: "div",
38796                 cls: "roo-layout-tools-button-inner " + className,
38797                 html: "&#160;"
38798             }]
38799         }, true);
38800         btn.addClassOnOver("roo-layout-tools-button-over");
38801         return btn;
38802     }
38803 });/*
38804  * Based on:
38805  * Ext JS Library 1.1.1
38806  * Copyright(c) 2006-2007, Ext JS, LLC.
38807  *
38808  * Originally Released Under LGPL - original licence link has changed is not relivant.
38809  *
38810  * Fork - LGPL
38811  * <script type="text/javascript">
38812  */
38813  
38814
38815
38816 /**
38817  * @class Roo.SplitLayoutRegion
38818  * @extends Roo.LayoutRegion
38819  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38820  */
38821 Roo.bootstrap.layout.Split = function(config){
38822     this.cursor = config.cursor;
38823     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38824 };
38825
38826 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38827 {
38828     splitTip : "Drag to resize.",
38829     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38830     useSplitTips : false,
38831
38832     applyConfig : function(config){
38833         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38834     },
38835     
38836     onRender : function(ctr,pos) {
38837         
38838         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38839         if(!this.config.split){
38840             return;
38841         }
38842         if(!this.split){
38843             
38844             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38845                             tag: "div",
38846                             id: this.el.id + "-split",
38847                             cls: "roo-layout-split roo-layout-split-"+this.position,
38848                             html: "&#160;"
38849             });
38850             /** The SplitBar for this region 
38851             * @type Roo.SplitBar */
38852             // does not exist yet...
38853             Roo.log([this.position, this.orientation]);
38854             
38855             this.split = new Roo.bootstrap.SplitBar({
38856                 dragElement : splitEl,
38857                 resizingElement: this.el,
38858                 orientation : this.orientation
38859             });
38860             
38861             this.split.on("moved", this.onSplitMove, this);
38862             this.split.useShim = this.config.useShim === true;
38863             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38864             if(this.useSplitTips){
38865                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38866             }
38867             //if(config.collapsible){
38868             //    this.split.el.on("dblclick", this.collapse,  this);
38869             //}
38870         }
38871         if(typeof this.config.minSize != "undefined"){
38872             this.split.minSize = this.config.minSize;
38873         }
38874         if(typeof this.config.maxSize != "undefined"){
38875             this.split.maxSize = this.config.maxSize;
38876         }
38877         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38878             this.hideSplitter();
38879         }
38880         
38881     },
38882
38883     getHMaxSize : function(){
38884          var cmax = this.config.maxSize || 10000;
38885          var center = this.mgr.getRegion("center");
38886          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38887     },
38888
38889     getVMaxSize : function(){
38890          var cmax = this.config.maxSize || 10000;
38891          var center = this.mgr.getRegion("center");
38892          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38893     },
38894
38895     onSplitMove : function(split, newSize){
38896         this.fireEvent("resized", this, newSize);
38897     },
38898     
38899     /** 
38900      * Returns the {@link Roo.SplitBar} for this region.
38901      * @return {Roo.SplitBar}
38902      */
38903     getSplitBar : function(){
38904         return this.split;
38905     },
38906     
38907     hide : function(){
38908         this.hideSplitter();
38909         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38910     },
38911
38912     hideSplitter : function(){
38913         if(this.split){
38914             this.split.el.setLocation(-2000,-2000);
38915             this.split.el.hide();
38916         }
38917     },
38918
38919     show : function(){
38920         if(this.split){
38921             this.split.el.show();
38922         }
38923         Roo.bootstrap.layout.Split.superclass.show.call(this);
38924     },
38925     
38926     beforeSlide: function(){
38927         if(Roo.isGecko){// firefox overflow auto bug workaround
38928             this.bodyEl.clip();
38929             if(this.tabs) {
38930                 this.tabs.bodyEl.clip();
38931             }
38932             if(this.activePanel){
38933                 this.activePanel.getEl().clip();
38934                 
38935                 if(this.activePanel.beforeSlide){
38936                     this.activePanel.beforeSlide();
38937                 }
38938             }
38939         }
38940     },
38941     
38942     afterSlide : function(){
38943         if(Roo.isGecko){// firefox overflow auto bug workaround
38944             this.bodyEl.unclip();
38945             if(this.tabs) {
38946                 this.tabs.bodyEl.unclip();
38947             }
38948             if(this.activePanel){
38949                 this.activePanel.getEl().unclip();
38950                 if(this.activePanel.afterSlide){
38951                     this.activePanel.afterSlide();
38952                 }
38953             }
38954         }
38955     },
38956
38957     initAutoHide : function(){
38958         if(this.autoHide !== false){
38959             if(!this.autoHideHd){
38960                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38961                 this.autoHideHd = {
38962                     "mouseout": function(e){
38963                         if(!e.within(this.el, true)){
38964                             st.delay(500);
38965                         }
38966                     },
38967                     "mouseover" : function(e){
38968                         st.cancel();
38969                     },
38970                     scope : this
38971                 };
38972             }
38973             this.el.on(this.autoHideHd);
38974         }
38975     },
38976
38977     clearAutoHide : function(){
38978         if(this.autoHide !== false){
38979             this.el.un("mouseout", this.autoHideHd.mouseout);
38980             this.el.un("mouseover", this.autoHideHd.mouseover);
38981         }
38982     },
38983
38984     clearMonitor : function(){
38985         Roo.get(document).un("click", this.slideInIf, this);
38986     },
38987
38988     // these names are backwards but not changed for compat
38989     slideOut : function(){
38990         if(this.isSlid || this.el.hasActiveFx()){
38991             return;
38992         }
38993         this.isSlid = true;
38994         if(this.collapseBtn){
38995             this.collapseBtn.hide();
38996         }
38997         this.closeBtnState = this.closeBtn.getStyle('display');
38998         this.closeBtn.hide();
38999         if(this.stickBtn){
39000             this.stickBtn.show();
39001         }
39002         this.el.show();
39003         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39004         this.beforeSlide();
39005         this.el.setStyle("z-index", 10001);
39006         this.el.slideIn(this.getSlideAnchor(), {
39007             callback: function(){
39008                 this.afterSlide();
39009                 this.initAutoHide();
39010                 Roo.get(document).on("click", this.slideInIf, this);
39011                 this.fireEvent("slideshow", this);
39012             },
39013             scope: this,
39014             block: true
39015         });
39016     },
39017
39018     afterSlideIn : function(){
39019         this.clearAutoHide();
39020         this.isSlid = false;
39021         this.clearMonitor();
39022         this.el.setStyle("z-index", "");
39023         if(this.collapseBtn){
39024             this.collapseBtn.show();
39025         }
39026         this.closeBtn.setStyle('display', this.closeBtnState);
39027         if(this.stickBtn){
39028             this.stickBtn.hide();
39029         }
39030         this.fireEvent("slidehide", this);
39031     },
39032
39033     slideIn : function(cb){
39034         if(!this.isSlid || this.el.hasActiveFx()){
39035             Roo.callback(cb);
39036             return;
39037         }
39038         this.isSlid = false;
39039         this.beforeSlide();
39040         this.el.slideOut(this.getSlideAnchor(), {
39041             callback: function(){
39042                 this.el.setLeftTop(-10000, -10000);
39043                 this.afterSlide();
39044                 this.afterSlideIn();
39045                 Roo.callback(cb);
39046             },
39047             scope: this,
39048             block: true
39049         });
39050     },
39051     
39052     slideInIf : function(e){
39053         if(!e.within(this.el)){
39054             this.slideIn();
39055         }
39056     },
39057
39058     animateCollapse : function(){
39059         this.beforeSlide();
39060         this.el.setStyle("z-index", 20000);
39061         var anchor = this.getSlideAnchor();
39062         this.el.slideOut(anchor, {
39063             callback : function(){
39064                 this.el.setStyle("z-index", "");
39065                 this.collapsedEl.slideIn(anchor, {duration:.3});
39066                 this.afterSlide();
39067                 this.el.setLocation(-10000,-10000);
39068                 this.el.hide();
39069                 this.fireEvent("collapsed", this);
39070             },
39071             scope: this,
39072             block: true
39073         });
39074     },
39075
39076     animateExpand : function(){
39077         this.beforeSlide();
39078         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39079         this.el.setStyle("z-index", 20000);
39080         this.collapsedEl.hide({
39081             duration:.1
39082         });
39083         this.el.slideIn(this.getSlideAnchor(), {
39084             callback : function(){
39085                 this.el.setStyle("z-index", "");
39086                 this.afterSlide();
39087                 if(this.split){
39088                     this.split.el.show();
39089                 }
39090                 this.fireEvent("invalidated", this);
39091                 this.fireEvent("expanded", this);
39092             },
39093             scope: this,
39094             block: true
39095         });
39096     },
39097
39098     anchors : {
39099         "west" : "left",
39100         "east" : "right",
39101         "north" : "top",
39102         "south" : "bottom"
39103     },
39104
39105     sanchors : {
39106         "west" : "l",
39107         "east" : "r",
39108         "north" : "t",
39109         "south" : "b"
39110     },
39111
39112     canchors : {
39113         "west" : "tl-tr",
39114         "east" : "tr-tl",
39115         "north" : "tl-bl",
39116         "south" : "bl-tl"
39117     },
39118
39119     getAnchor : function(){
39120         return this.anchors[this.position];
39121     },
39122
39123     getCollapseAnchor : function(){
39124         return this.canchors[this.position];
39125     },
39126
39127     getSlideAnchor : function(){
39128         return this.sanchors[this.position];
39129     },
39130
39131     getAlignAdj : function(){
39132         var cm = this.cmargins;
39133         switch(this.position){
39134             case "west":
39135                 return [0, 0];
39136             break;
39137             case "east":
39138                 return [0, 0];
39139             break;
39140             case "north":
39141                 return [0, 0];
39142             break;
39143             case "south":
39144                 return [0, 0];
39145             break;
39146         }
39147     },
39148
39149     getExpandAdj : function(){
39150         var c = this.collapsedEl, cm = this.cmargins;
39151         switch(this.position){
39152             case "west":
39153                 return [-(cm.right+c.getWidth()+cm.left), 0];
39154             break;
39155             case "east":
39156                 return [cm.right+c.getWidth()+cm.left, 0];
39157             break;
39158             case "north":
39159                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39160             break;
39161             case "south":
39162                 return [0, cm.top+cm.bottom+c.getHeight()];
39163             break;
39164         }
39165     }
39166 });/*
39167  * Based on:
39168  * Ext JS Library 1.1.1
39169  * Copyright(c) 2006-2007, Ext JS, LLC.
39170  *
39171  * Originally Released Under LGPL - original licence link has changed is not relivant.
39172  *
39173  * Fork - LGPL
39174  * <script type="text/javascript">
39175  */
39176 /*
39177  * These classes are private internal classes
39178  */
39179 Roo.bootstrap.layout.Center = function(config){
39180     config.region = "center";
39181     Roo.bootstrap.layout.Region.call(this, config);
39182     this.visible = true;
39183     this.minWidth = config.minWidth || 20;
39184     this.minHeight = config.minHeight || 20;
39185 };
39186
39187 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39188     hide : function(){
39189         // center panel can't be hidden
39190     },
39191     
39192     show : function(){
39193         // center panel can't be hidden
39194     },
39195     
39196     getMinWidth: function(){
39197         return this.minWidth;
39198     },
39199     
39200     getMinHeight: function(){
39201         return this.minHeight;
39202     }
39203 });
39204
39205
39206
39207
39208  
39209
39210
39211
39212
39213
39214
39215 Roo.bootstrap.layout.North = function(config)
39216 {
39217     config.region = 'north';
39218     config.cursor = 'n-resize';
39219     
39220     Roo.bootstrap.layout.Split.call(this, config);
39221     
39222     
39223     if(this.split){
39224         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39225         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39226         this.split.el.addClass("roo-layout-split-v");
39227     }
39228     var size = config.initialSize || config.height;
39229     if(typeof size != "undefined"){
39230         this.el.setHeight(size);
39231     }
39232 };
39233 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39234 {
39235     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39236     
39237     
39238     
39239     getBox : function(){
39240         if(this.collapsed){
39241             return this.collapsedEl.getBox();
39242         }
39243         var box = this.el.getBox();
39244         if(this.split){
39245             box.height += this.split.el.getHeight();
39246         }
39247         return box;
39248     },
39249     
39250     updateBox : function(box){
39251         if(this.split && !this.collapsed){
39252             box.height -= this.split.el.getHeight();
39253             this.split.el.setLeft(box.x);
39254             this.split.el.setTop(box.y+box.height);
39255             this.split.el.setWidth(box.width);
39256         }
39257         if(this.collapsed){
39258             this.updateBody(box.width, null);
39259         }
39260         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39261     }
39262 });
39263
39264
39265
39266
39267
39268 Roo.bootstrap.layout.South = function(config){
39269     config.region = 'south';
39270     config.cursor = 's-resize';
39271     Roo.bootstrap.layout.Split.call(this, config);
39272     if(this.split){
39273         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39274         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39275         this.split.el.addClass("roo-layout-split-v");
39276     }
39277     var size = config.initialSize || config.height;
39278     if(typeof size != "undefined"){
39279         this.el.setHeight(size);
39280     }
39281 };
39282
39283 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39284     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39285     getBox : function(){
39286         if(this.collapsed){
39287             return this.collapsedEl.getBox();
39288         }
39289         var box = this.el.getBox();
39290         if(this.split){
39291             var sh = this.split.el.getHeight();
39292             box.height += sh;
39293             box.y -= sh;
39294         }
39295         return box;
39296     },
39297     
39298     updateBox : function(box){
39299         if(this.split && !this.collapsed){
39300             var sh = this.split.el.getHeight();
39301             box.height -= sh;
39302             box.y += sh;
39303             this.split.el.setLeft(box.x);
39304             this.split.el.setTop(box.y-sh);
39305             this.split.el.setWidth(box.width);
39306         }
39307         if(this.collapsed){
39308             this.updateBody(box.width, null);
39309         }
39310         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39311     }
39312 });
39313
39314 Roo.bootstrap.layout.East = function(config){
39315     config.region = "east";
39316     config.cursor = "e-resize";
39317     Roo.bootstrap.layout.Split.call(this, config);
39318     if(this.split){
39319         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39320         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39321         this.split.el.addClass("roo-layout-split-h");
39322     }
39323     var size = config.initialSize || config.width;
39324     if(typeof size != "undefined"){
39325         this.el.setWidth(size);
39326     }
39327 };
39328 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39329     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39330     getBox : function(){
39331         if(this.collapsed){
39332             return this.collapsedEl.getBox();
39333         }
39334         var box = this.el.getBox();
39335         if(this.split){
39336             var sw = this.split.el.getWidth();
39337             box.width += sw;
39338             box.x -= sw;
39339         }
39340         return box;
39341     },
39342
39343     updateBox : function(box){
39344         if(this.split && !this.collapsed){
39345             var sw = this.split.el.getWidth();
39346             box.width -= sw;
39347             this.split.el.setLeft(box.x);
39348             this.split.el.setTop(box.y);
39349             this.split.el.setHeight(box.height);
39350             box.x += sw;
39351         }
39352         if(this.collapsed){
39353             this.updateBody(null, box.height);
39354         }
39355         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39356     }
39357 });
39358
39359 Roo.bootstrap.layout.West = function(config){
39360     config.region = "west";
39361     config.cursor = "w-resize";
39362     
39363     Roo.bootstrap.layout.Split.call(this, config);
39364     if(this.split){
39365         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39366         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39367         this.split.el.addClass("roo-layout-split-h");
39368     }
39369     
39370 };
39371 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39372     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39373     
39374     onRender: function(ctr, pos)
39375     {
39376         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39377         var size = this.config.initialSize || this.config.width;
39378         if(typeof size != "undefined"){
39379             this.el.setWidth(size);
39380         }
39381     },
39382     
39383     getBox : function(){
39384         if(this.collapsed){
39385             return this.collapsedEl.getBox();
39386         }
39387         var box = this.el.getBox();
39388         if(this.split){
39389             box.width += this.split.el.getWidth();
39390         }
39391         return box;
39392     },
39393     
39394     updateBox : function(box){
39395         if(this.split && !this.collapsed){
39396             var sw = this.split.el.getWidth();
39397             box.width -= sw;
39398             this.split.el.setLeft(box.x+box.width);
39399             this.split.el.setTop(box.y);
39400             this.split.el.setHeight(box.height);
39401         }
39402         if(this.collapsed){
39403             this.updateBody(null, box.height);
39404         }
39405         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39406     }
39407 });Roo.namespace("Roo.bootstrap.panel");/*
39408  * Based on:
39409  * Ext JS Library 1.1.1
39410  * Copyright(c) 2006-2007, Ext JS, LLC.
39411  *
39412  * Originally Released Under LGPL - original licence link has changed is not relivant.
39413  *
39414  * Fork - LGPL
39415  * <script type="text/javascript">
39416  */
39417 /**
39418  * @class Roo.ContentPanel
39419  * @extends Roo.util.Observable
39420  * A basic ContentPanel element.
39421  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39422  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39423  * @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
39424  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39425  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39426  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39427  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39428  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39429  * @cfg {String} title          The title for this panel
39430  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39431  * @cfg {String} url            Calls {@link #setUrl} with this value
39432  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39433  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39434  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39435  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39436  * @cfg {Boolean} badges render the badges
39437  * @cfg {String} cls  extra classes to use  
39438  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39439
39440  * @constructor
39441  * Create a new ContentPanel.
39442  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39443  * @param {String/Object} config A string to set only the title or a config object
39444  * @param {String} content (optional) Set the HTML content for this panel
39445  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39446  */
39447 Roo.bootstrap.panel.Content = function( config){
39448     
39449     this.tpl = config.tpl || false;
39450     
39451     var el = config.el;
39452     var content = config.content;
39453
39454     if(config.autoCreate){ // xtype is available if this is called from factory
39455         el = Roo.id();
39456     }
39457     this.el = Roo.get(el);
39458     if(!this.el && config && config.autoCreate){
39459         if(typeof config.autoCreate == "object"){
39460             if(!config.autoCreate.id){
39461                 config.autoCreate.id = config.id||el;
39462             }
39463             this.el = Roo.DomHelper.append(document.body,
39464                         config.autoCreate, true);
39465         }else{
39466             var elcfg =  {
39467                 tag: "div",
39468                 cls: (config.cls || '') +
39469                     (config.background ? ' bg-' + config.background : '') +
39470                     " roo-layout-inactive-content",
39471                 id: config.id||el
39472             };
39473             if (config.html) {
39474                 elcfg.html = config.html;
39475                 
39476             }
39477                         
39478             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39479         }
39480     } 
39481     this.closable = false;
39482     this.loaded = false;
39483     this.active = false;
39484    
39485       
39486     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39487         
39488         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39489         
39490         this.wrapEl = this.el; //this.el.wrap();
39491         var ti = [];
39492         if (config.toolbar.items) {
39493             ti = config.toolbar.items ;
39494             delete config.toolbar.items ;
39495         }
39496         
39497         var nitems = [];
39498         this.toolbar.render(this.wrapEl, 'before');
39499         for(var i =0;i < ti.length;i++) {
39500           //  Roo.log(['add child', items[i]]);
39501             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39502         }
39503         this.toolbar.items = nitems;
39504         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39505         delete config.toolbar;
39506         
39507     }
39508     /*
39509     // xtype created footer. - not sure if will work as we normally have to render first..
39510     if (this.footer && !this.footer.el && this.footer.xtype) {
39511         if (!this.wrapEl) {
39512             this.wrapEl = this.el.wrap();
39513         }
39514     
39515         this.footer.container = this.wrapEl.createChild();
39516          
39517         this.footer = Roo.factory(this.footer, Roo);
39518         
39519     }
39520     */
39521     
39522      if(typeof config == "string"){
39523         this.title = config;
39524     }else{
39525         Roo.apply(this, config);
39526     }
39527     
39528     if(this.resizeEl){
39529         this.resizeEl = Roo.get(this.resizeEl, true);
39530     }else{
39531         this.resizeEl = this.el;
39532     }
39533     // handle view.xtype
39534     
39535  
39536     
39537     
39538     this.addEvents({
39539         /**
39540          * @event activate
39541          * Fires when this panel is activated. 
39542          * @param {Roo.ContentPanel} this
39543          */
39544         "activate" : true,
39545         /**
39546          * @event deactivate
39547          * Fires when this panel is activated. 
39548          * @param {Roo.ContentPanel} this
39549          */
39550         "deactivate" : true,
39551
39552         /**
39553          * @event resize
39554          * Fires when this panel is resized if fitToFrame is true.
39555          * @param {Roo.ContentPanel} this
39556          * @param {Number} width The width after any component adjustments
39557          * @param {Number} height The height after any component adjustments
39558          */
39559         "resize" : true,
39560         
39561          /**
39562          * @event render
39563          * Fires when this tab is created
39564          * @param {Roo.ContentPanel} this
39565          */
39566         "render" : true
39567         
39568         
39569         
39570     });
39571     
39572
39573     
39574     
39575     if(this.autoScroll){
39576         this.resizeEl.setStyle("overflow", "auto");
39577     } else {
39578         // fix randome scrolling
39579         //this.el.on('scroll', function() {
39580         //    Roo.log('fix random scolling');
39581         //    this.scrollTo('top',0); 
39582         //});
39583     }
39584     content = content || this.content;
39585     if(content){
39586         this.setContent(content);
39587     }
39588     if(config && config.url){
39589         this.setUrl(this.url, this.params, this.loadOnce);
39590     }
39591     
39592     
39593     
39594     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39595     
39596     if (this.view && typeof(this.view.xtype) != 'undefined') {
39597         this.view.el = this.el.appendChild(document.createElement("div"));
39598         this.view = Roo.factory(this.view); 
39599         this.view.render  &&  this.view.render(false, '');  
39600     }
39601     
39602     
39603     this.fireEvent('render', this);
39604 };
39605
39606 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39607     
39608     cls : '',
39609     background : '',
39610     
39611     tabTip : '',
39612     
39613     setRegion : function(region){
39614         this.region = region;
39615         this.setActiveClass(region && !this.background);
39616     },
39617     
39618     
39619     setActiveClass: function(state)
39620     {
39621         if(state){
39622            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39623            this.el.setStyle('position','relative');
39624         }else{
39625            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39626            this.el.setStyle('position', 'absolute');
39627         } 
39628     },
39629     
39630     /**
39631      * Returns the toolbar for this Panel if one was configured. 
39632      * @return {Roo.Toolbar} 
39633      */
39634     getToolbar : function(){
39635         return this.toolbar;
39636     },
39637     
39638     setActiveState : function(active)
39639     {
39640         this.active = active;
39641         this.setActiveClass(active);
39642         if(!active){
39643             if(this.fireEvent("deactivate", this) === false){
39644                 return false;
39645             }
39646             return true;
39647         }
39648         this.fireEvent("activate", this);
39649         return true;
39650     },
39651     /**
39652      * Updates this panel's element
39653      * @param {String} content The new content
39654      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39655     */
39656     setContent : function(content, loadScripts){
39657         this.el.update(content, loadScripts);
39658     },
39659
39660     ignoreResize : function(w, h){
39661         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39662             return true;
39663         }else{
39664             this.lastSize = {width: w, height: h};
39665             return false;
39666         }
39667     },
39668     /**
39669      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39670      * @return {Roo.UpdateManager} The UpdateManager
39671      */
39672     getUpdateManager : function(){
39673         return this.el.getUpdateManager();
39674     },
39675      /**
39676      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39677      * @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:
39678 <pre><code>
39679 panel.load({
39680     url: "your-url.php",
39681     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39682     callback: yourFunction,
39683     scope: yourObject, //(optional scope)
39684     discardUrl: false,
39685     nocache: false,
39686     text: "Loading...",
39687     timeout: 30,
39688     scripts: false
39689 });
39690 </code></pre>
39691      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39692      * 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.
39693      * @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}
39694      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39695      * @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.
39696      * @return {Roo.ContentPanel} this
39697      */
39698     load : function(){
39699         var um = this.el.getUpdateManager();
39700         um.update.apply(um, arguments);
39701         return this;
39702     },
39703
39704
39705     /**
39706      * 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.
39707      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39708      * @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)
39709      * @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)
39710      * @return {Roo.UpdateManager} The UpdateManager
39711      */
39712     setUrl : function(url, params, loadOnce){
39713         if(this.refreshDelegate){
39714             this.removeListener("activate", this.refreshDelegate);
39715         }
39716         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39717         this.on("activate", this.refreshDelegate);
39718         return this.el.getUpdateManager();
39719     },
39720     
39721     _handleRefresh : function(url, params, loadOnce){
39722         if(!loadOnce || !this.loaded){
39723             var updater = this.el.getUpdateManager();
39724             updater.update(url, params, this._setLoaded.createDelegate(this));
39725         }
39726     },
39727     
39728     _setLoaded : function(){
39729         this.loaded = true;
39730     }, 
39731     
39732     /**
39733      * Returns this panel's id
39734      * @return {String} 
39735      */
39736     getId : function(){
39737         return this.el.id;
39738     },
39739     
39740     /** 
39741      * Returns this panel's element - used by regiosn to add.
39742      * @return {Roo.Element} 
39743      */
39744     getEl : function(){
39745         return this.wrapEl || this.el;
39746     },
39747     
39748    
39749     
39750     adjustForComponents : function(width, height)
39751     {
39752         //Roo.log('adjustForComponents ');
39753         if(this.resizeEl != this.el){
39754             width -= this.el.getFrameWidth('lr');
39755             height -= this.el.getFrameWidth('tb');
39756         }
39757         if(this.toolbar){
39758             var te = this.toolbar.getEl();
39759             te.setWidth(width);
39760             height -= te.getHeight();
39761         }
39762         if(this.footer){
39763             var te = this.footer.getEl();
39764             te.setWidth(width);
39765             height -= te.getHeight();
39766         }
39767         
39768         
39769         if(this.adjustments){
39770             width += this.adjustments[0];
39771             height += this.adjustments[1];
39772         }
39773         return {"width": width, "height": height};
39774     },
39775     
39776     setSize : function(width, height){
39777         if(this.fitToFrame && !this.ignoreResize(width, height)){
39778             if(this.fitContainer && this.resizeEl != this.el){
39779                 this.el.setSize(width, height);
39780             }
39781             var size = this.adjustForComponents(width, height);
39782             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39783             this.fireEvent('resize', this, size.width, size.height);
39784         }
39785     },
39786     
39787     /**
39788      * Returns this panel's title
39789      * @return {String} 
39790      */
39791     getTitle : function(){
39792         
39793         if (typeof(this.title) != 'object') {
39794             return this.title;
39795         }
39796         
39797         var t = '';
39798         for (var k in this.title) {
39799             if (!this.title.hasOwnProperty(k)) {
39800                 continue;
39801             }
39802             
39803             if (k.indexOf('-') >= 0) {
39804                 var s = k.split('-');
39805                 for (var i = 0; i<s.length; i++) {
39806                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39807                 }
39808             } else {
39809                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39810             }
39811         }
39812         return t;
39813     },
39814     
39815     /**
39816      * Set this panel's title
39817      * @param {String} title
39818      */
39819     setTitle : function(title){
39820         this.title = title;
39821         if(this.region){
39822             this.region.updatePanelTitle(this, title);
39823         }
39824     },
39825     
39826     /**
39827      * Returns true is this panel was configured to be closable
39828      * @return {Boolean} 
39829      */
39830     isClosable : function(){
39831         return this.closable;
39832     },
39833     
39834     beforeSlide : function(){
39835         this.el.clip();
39836         this.resizeEl.clip();
39837     },
39838     
39839     afterSlide : function(){
39840         this.el.unclip();
39841         this.resizeEl.unclip();
39842     },
39843     
39844     /**
39845      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39846      *   Will fail silently if the {@link #setUrl} method has not been called.
39847      *   This does not activate the panel, just updates its content.
39848      */
39849     refresh : function(){
39850         if(this.refreshDelegate){
39851            this.loaded = false;
39852            this.refreshDelegate();
39853         }
39854     },
39855     
39856     /**
39857      * Destroys this panel
39858      */
39859     destroy : function(){
39860         this.el.removeAllListeners();
39861         var tempEl = document.createElement("span");
39862         tempEl.appendChild(this.el.dom);
39863         tempEl.innerHTML = "";
39864         this.el.remove();
39865         this.el = null;
39866     },
39867     
39868     /**
39869      * form - if the content panel contains a form - this is a reference to it.
39870      * @type {Roo.form.Form}
39871      */
39872     form : false,
39873     /**
39874      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39875      *    This contains a reference to it.
39876      * @type {Roo.View}
39877      */
39878     view : false,
39879     
39880       /**
39881      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39882      * <pre><code>
39883
39884 layout.addxtype({
39885        xtype : 'Form',
39886        items: [ .... ]
39887    }
39888 );
39889
39890 </code></pre>
39891      * @param {Object} cfg Xtype definition of item to add.
39892      */
39893     
39894     
39895     getChildContainer: function () {
39896         return this.getEl();
39897     }
39898     
39899     
39900     /*
39901         var  ret = new Roo.factory(cfg);
39902         return ret;
39903         
39904         
39905         // add form..
39906         if (cfg.xtype.match(/^Form$/)) {
39907             
39908             var el;
39909             //if (this.footer) {
39910             //    el = this.footer.container.insertSibling(false, 'before');
39911             //} else {
39912                 el = this.el.createChild();
39913             //}
39914
39915             this.form = new  Roo.form.Form(cfg);
39916             
39917             
39918             if ( this.form.allItems.length) {
39919                 this.form.render(el.dom);
39920             }
39921             return this.form;
39922         }
39923         // should only have one of theses..
39924         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39925             // views.. should not be just added - used named prop 'view''
39926             
39927             cfg.el = this.el.appendChild(document.createElement("div"));
39928             // factory?
39929             
39930             var ret = new Roo.factory(cfg);
39931              
39932              ret.render && ret.render(false, ''); // render blank..
39933             this.view = ret;
39934             return ret;
39935         }
39936         return false;
39937     }
39938     \*/
39939 });
39940  
39941 /**
39942  * @class Roo.bootstrap.panel.Grid
39943  * @extends Roo.bootstrap.panel.Content
39944  * @constructor
39945  * Create a new GridPanel.
39946  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39947  * @param {Object} config A the config object
39948   
39949  */
39950
39951
39952
39953 Roo.bootstrap.panel.Grid = function(config)
39954 {
39955     
39956       
39957     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39958         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39959
39960     config.el = this.wrapper;
39961     //this.el = this.wrapper;
39962     
39963       if (config.container) {
39964         // ctor'ed from a Border/panel.grid
39965         
39966         
39967         this.wrapper.setStyle("overflow", "hidden");
39968         this.wrapper.addClass('roo-grid-container');
39969
39970     }
39971     
39972     
39973     if(config.toolbar){
39974         var tool_el = this.wrapper.createChild();    
39975         this.toolbar = Roo.factory(config.toolbar);
39976         var ti = [];
39977         if (config.toolbar.items) {
39978             ti = config.toolbar.items ;
39979             delete config.toolbar.items ;
39980         }
39981         
39982         var nitems = [];
39983         this.toolbar.render(tool_el);
39984         for(var i =0;i < ti.length;i++) {
39985           //  Roo.log(['add child', items[i]]);
39986             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39987         }
39988         this.toolbar.items = nitems;
39989         
39990         delete config.toolbar;
39991     }
39992     
39993     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39994     config.grid.scrollBody = true;;
39995     config.grid.monitorWindowResize = false; // turn off autosizing
39996     config.grid.autoHeight = false;
39997     config.grid.autoWidth = false;
39998     
39999     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40000     
40001     if (config.background) {
40002         // render grid on panel activation (if panel background)
40003         this.on('activate', function(gp) {
40004             if (!gp.grid.rendered) {
40005                 gp.grid.render(this.wrapper);
40006                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40007             }
40008         });
40009             
40010     } else {
40011         this.grid.render(this.wrapper);
40012         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40013
40014     }
40015     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40016     // ??? needed ??? config.el = this.wrapper;
40017     
40018     
40019     
40020   
40021     // xtype created footer. - not sure if will work as we normally have to render first..
40022     if (this.footer && !this.footer.el && this.footer.xtype) {
40023         
40024         var ctr = this.grid.getView().getFooterPanel(true);
40025         this.footer.dataSource = this.grid.dataSource;
40026         this.footer = Roo.factory(this.footer, Roo);
40027         this.footer.render(ctr);
40028         
40029     }
40030     
40031     
40032     
40033     
40034      
40035 };
40036
40037 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40038     getId : function(){
40039         return this.grid.id;
40040     },
40041     
40042     /**
40043      * Returns the grid for this panel
40044      * @return {Roo.bootstrap.Table} 
40045      */
40046     getGrid : function(){
40047         return this.grid;    
40048     },
40049     
40050     setSize : function(width, height){
40051         if(!this.ignoreResize(width, height)){
40052             var grid = this.grid;
40053             var size = this.adjustForComponents(width, height);
40054             // tfoot is not a footer?
40055           
40056             
40057             var gridel = grid.getGridEl();
40058             gridel.setSize(size.width, size.height);
40059             
40060             var tbd = grid.getGridEl().select('tbody', true).first();
40061             var thd = grid.getGridEl().select('thead',true).first();
40062             var tbf= grid.getGridEl().select('tfoot', true).first();
40063
40064             if (tbf) {
40065                 size.height -= thd.getHeight();
40066             }
40067             if (thd) {
40068                 size.height -= thd.getHeight();
40069             }
40070             
40071             tbd.setSize(size.width, size.height );
40072             // this is for the account management tab -seems to work there.
40073             var thd = grid.getGridEl().select('thead',true).first();
40074             //if (tbd) {
40075             //    tbd.setSize(size.width, size.height - thd.getHeight());
40076             //}
40077              
40078             grid.autoSize();
40079         }
40080     },
40081      
40082     
40083     
40084     beforeSlide : function(){
40085         this.grid.getView().scroller.clip();
40086     },
40087     
40088     afterSlide : function(){
40089         this.grid.getView().scroller.unclip();
40090     },
40091     
40092     destroy : function(){
40093         this.grid.destroy();
40094         delete this.grid;
40095         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40096     }
40097 });
40098
40099 /**
40100  * @class Roo.bootstrap.panel.Nest
40101  * @extends Roo.bootstrap.panel.Content
40102  * @constructor
40103  * Create a new Panel, that can contain a layout.Border.
40104  * 
40105  * 
40106  * @param {Roo.BorderLayout} layout The layout for this panel
40107  * @param {String/Object} config A string to set only the title or a config object
40108  */
40109 Roo.bootstrap.panel.Nest = function(config)
40110 {
40111     // construct with only one argument..
40112     /* FIXME - implement nicer consturctors
40113     if (layout.layout) {
40114         config = layout;
40115         layout = config.layout;
40116         delete config.layout;
40117     }
40118     if (layout.xtype && !layout.getEl) {
40119         // then layout needs constructing..
40120         layout = Roo.factory(layout, Roo);
40121     }
40122     */
40123     
40124     config.el =  config.layout.getEl();
40125     
40126     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40127     
40128     config.layout.monitorWindowResize = false; // turn off autosizing
40129     this.layout = config.layout;
40130     this.layout.getEl().addClass("roo-layout-nested-layout");
40131     this.layout.parent = this;
40132     
40133     
40134     
40135     
40136 };
40137
40138 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40139
40140     setSize : function(width, height){
40141         if(!this.ignoreResize(width, height)){
40142             var size = this.adjustForComponents(width, height);
40143             var el = this.layout.getEl();
40144             if (size.height < 1) {
40145                 el.setWidth(size.width);   
40146             } else {
40147                 el.setSize(size.width, size.height);
40148             }
40149             var touch = el.dom.offsetWidth;
40150             this.layout.layout();
40151             // ie requires a double layout on the first pass
40152             if(Roo.isIE && !this.initialized){
40153                 this.initialized = true;
40154                 this.layout.layout();
40155             }
40156         }
40157     },
40158     
40159     // activate all subpanels if not currently active..
40160     
40161     setActiveState : function(active){
40162         this.active = active;
40163         this.setActiveClass(active);
40164         
40165         if(!active){
40166             this.fireEvent("deactivate", this);
40167             return;
40168         }
40169         
40170         this.fireEvent("activate", this);
40171         // not sure if this should happen before or after..
40172         if (!this.layout) {
40173             return; // should not happen..
40174         }
40175         var reg = false;
40176         for (var r in this.layout.regions) {
40177             reg = this.layout.getRegion(r);
40178             if (reg.getActivePanel()) {
40179                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40180                 reg.setActivePanel(reg.getActivePanel());
40181                 continue;
40182             }
40183             if (!reg.panels.length) {
40184                 continue;
40185             }
40186             reg.showPanel(reg.getPanel(0));
40187         }
40188         
40189         
40190         
40191         
40192     },
40193     
40194     /**
40195      * Returns the nested BorderLayout for this panel
40196      * @return {Roo.BorderLayout} 
40197      */
40198     getLayout : function(){
40199         return this.layout;
40200     },
40201     
40202      /**
40203      * Adds a xtype elements to the layout of the nested panel
40204      * <pre><code>
40205
40206 panel.addxtype({
40207        xtype : 'ContentPanel',
40208        region: 'west',
40209        items: [ .... ]
40210    }
40211 );
40212
40213 panel.addxtype({
40214         xtype : 'NestedLayoutPanel',
40215         region: 'west',
40216         layout: {
40217            center: { },
40218            west: { }   
40219         },
40220         items : [ ... list of content panels or nested layout panels.. ]
40221    }
40222 );
40223 </code></pre>
40224      * @param {Object} cfg Xtype definition of item to add.
40225      */
40226     addxtype : function(cfg) {
40227         return this.layout.addxtype(cfg);
40228     
40229     }
40230 });/*
40231  * Based on:
40232  * Ext JS Library 1.1.1
40233  * Copyright(c) 2006-2007, Ext JS, LLC.
40234  *
40235  * Originally Released Under LGPL - original licence link has changed is not relivant.
40236  *
40237  * Fork - LGPL
40238  * <script type="text/javascript">
40239  */
40240 /**
40241  * @class Roo.TabPanel
40242  * @extends Roo.util.Observable
40243  * A lightweight tab container.
40244  * <br><br>
40245  * Usage:
40246  * <pre><code>
40247 // basic tabs 1, built from existing content
40248 var tabs = new Roo.TabPanel("tabs1");
40249 tabs.addTab("script", "View Script");
40250 tabs.addTab("markup", "View Markup");
40251 tabs.activate("script");
40252
40253 // more advanced tabs, built from javascript
40254 var jtabs = new Roo.TabPanel("jtabs");
40255 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40256
40257 // set up the UpdateManager
40258 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40259 var updater = tab2.getUpdateManager();
40260 updater.setDefaultUrl("ajax1.htm");
40261 tab2.on('activate', updater.refresh, updater, true);
40262
40263 // Use setUrl for Ajax loading
40264 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40265 tab3.setUrl("ajax2.htm", null, true);
40266
40267 // Disabled tab
40268 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40269 tab4.disable();
40270
40271 jtabs.activate("jtabs-1");
40272  * </code></pre>
40273  * @constructor
40274  * Create a new TabPanel.
40275  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40276  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40277  */
40278 Roo.bootstrap.panel.Tabs = function(config){
40279     /**
40280     * The container element for this TabPanel.
40281     * @type Roo.Element
40282     */
40283     this.el = Roo.get(config.el);
40284     delete config.el;
40285     if(config){
40286         if(typeof config == "boolean"){
40287             this.tabPosition = config ? "bottom" : "top";
40288         }else{
40289             Roo.apply(this, config);
40290         }
40291     }
40292     
40293     if(this.tabPosition == "bottom"){
40294         // if tabs are at the bottom = create the body first.
40295         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40296         this.el.addClass("roo-tabs-bottom");
40297     }
40298     // next create the tabs holders
40299     
40300     if (this.tabPosition == "west"){
40301         
40302         var reg = this.region; // fake it..
40303         while (reg) {
40304             if (!reg.mgr.parent) {
40305                 break;
40306             }
40307             reg = reg.mgr.parent.region;
40308         }
40309         Roo.log("got nest?");
40310         Roo.log(reg);
40311         if (reg.mgr.getRegion('west')) {
40312             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40313             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40314             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40315             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40316             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40317         
40318             
40319         }
40320         
40321         
40322     } else {
40323      
40324         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40325         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40326         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40327         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40328     }
40329     
40330     
40331     if(Roo.isIE){
40332         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40333     }
40334     
40335     // finally - if tabs are at the top, then create the body last..
40336     if(this.tabPosition != "bottom"){
40337         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40338          * @type Roo.Element
40339          */
40340         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40341         this.el.addClass("roo-tabs-top");
40342     }
40343     this.items = [];
40344
40345     this.bodyEl.setStyle("position", "relative");
40346
40347     this.active = null;
40348     this.activateDelegate = this.activate.createDelegate(this);
40349
40350     this.addEvents({
40351         /**
40352          * @event tabchange
40353          * Fires when the active tab changes
40354          * @param {Roo.TabPanel} this
40355          * @param {Roo.TabPanelItem} activePanel The new active tab
40356          */
40357         "tabchange": true,
40358         /**
40359          * @event beforetabchange
40360          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40361          * @param {Roo.TabPanel} this
40362          * @param {Object} e Set cancel to true on this object to cancel the tab change
40363          * @param {Roo.TabPanelItem} tab The tab being changed to
40364          */
40365         "beforetabchange" : true
40366     });
40367
40368     Roo.EventManager.onWindowResize(this.onResize, this);
40369     this.cpad = this.el.getPadding("lr");
40370     this.hiddenCount = 0;
40371
40372
40373     // toolbar on the tabbar support...
40374     if (this.toolbar) {
40375         alert("no toolbar support yet");
40376         this.toolbar  = false;
40377         /*
40378         var tcfg = this.toolbar;
40379         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40380         this.toolbar = new Roo.Toolbar(tcfg);
40381         if (Roo.isSafari) {
40382             var tbl = tcfg.container.child('table', true);
40383             tbl.setAttribute('width', '100%');
40384         }
40385         */
40386         
40387     }
40388    
40389
40390
40391     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40392 };
40393
40394 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40395     /*
40396      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40397      */
40398     tabPosition : "top",
40399     /*
40400      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40401      */
40402     currentTabWidth : 0,
40403     /*
40404      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40405      */
40406     minTabWidth : 40,
40407     /*
40408      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40409      */
40410     maxTabWidth : 250,
40411     /*
40412      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40413      */
40414     preferredTabWidth : 175,
40415     /*
40416      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40417      */
40418     resizeTabs : false,
40419     /*
40420      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40421      */
40422     monitorResize : true,
40423     /*
40424      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40425      */
40426     toolbar : false,  // set by caller..
40427     
40428     region : false, /// set by caller
40429     
40430     disableTooltips : true, // not used yet...
40431
40432     /**
40433      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40434      * @param {String} id The id of the div to use <b>or create</b>
40435      * @param {String} text The text for the tab
40436      * @param {String} content (optional) Content to put in the TabPanelItem body
40437      * @param {Boolean} closable (optional) True to create a close icon on the tab
40438      * @return {Roo.TabPanelItem} The created TabPanelItem
40439      */
40440     addTab : function(id, text, content, closable, tpl)
40441     {
40442         var item = new Roo.bootstrap.panel.TabItem({
40443             panel: this,
40444             id : id,
40445             text : text,
40446             closable : closable,
40447             tpl : tpl
40448         });
40449         this.addTabItem(item);
40450         if(content){
40451             item.setContent(content);
40452         }
40453         return item;
40454     },
40455
40456     /**
40457      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40458      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40459      * @return {Roo.TabPanelItem}
40460      */
40461     getTab : function(id){
40462         return this.items[id];
40463     },
40464
40465     /**
40466      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40467      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40468      */
40469     hideTab : function(id){
40470         var t = this.items[id];
40471         if(!t.isHidden()){
40472            t.setHidden(true);
40473            this.hiddenCount++;
40474            this.autoSizeTabs();
40475         }
40476     },
40477
40478     /**
40479      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40480      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40481      */
40482     unhideTab : function(id){
40483         var t = this.items[id];
40484         if(t.isHidden()){
40485            t.setHidden(false);
40486            this.hiddenCount--;
40487            this.autoSizeTabs();
40488         }
40489     },
40490
40491     /**
40492      * Adds an existing {@link Roo.TabPanelItem}.
40493      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40494      */
40495     addTabItem : function(item)
40496     {
40497         this.items[item.id] = item;
40498         this.items.push(item);
40499         this.autoSizeTabs();
40500       //  if(this.resizeTabs){
40501     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40502   //         this.autoSizeTabs();
40503 //        }else{
40504 //            item.autoSize();
40505        // }
40506     },
40507
40508     /**
40509      * Removes a {@link Roo.TabPanelItem}.
40510      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40511      */
40512     removeTab : function(id){
40513         var items = this.items;
40514         var tab = items[id];
40515         if(!tab) { return; }
40516         var index = items.indexOf(tab);
40517         if(this.active == tab && items.length > 1){
40518             var newTab = this.getNextAvailable(index);
40519             if(newTab) {
40520                 newTab.activate();
40521             }
40522         }
40523         this.stripEl.dom.removeChild(tab.pnode.dom);
40524         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40525             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40526         }
40527         items.splice(index, 1);
40528         delete this.items[tab.id];
40529         tab.fireEvent("close", tab);
40530         tab.purgeListeners();
40531         this.autoSizeTabs();
40532     },
40533
40534     getNextAvailable : function(start){
40535         var items = this.items;
40536         var index = start;
40537         // look for a next tab that will slide over to
40538         // replace the one being removed
40539         while(index < items.length){
40540             var item = items[++index];
40541             if(item && !item.isHidden()){
40542                 return item;
40543             }
40544         }
40545         // if one isn't found select the previous tab (on the left)
40546         index = start;
40547         while(index >= 0){
40548             var item = items[--index];
40549             if(item && !item.isHidden()){
40550                 return item;
40551             }
40552         }
40553         return null;
40554     },
40555
40556     /**
40557      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40558      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40559      */
40560     disableTab : function(id){
40561         var tab = this.items[id];
40562         if(tab && this.active != tab){
40563             tab.disable();
40564         }
40565     },
40566
40567     /**
40568      * Enables a {@link Roo.TabPanelItem} that is disabled.
40569      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40570      */
40571     enableTab : function(id){
40572         var tab = this.items[id];
40573         tab.enable();
40574     },
40575
40576     /**
40577      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40578      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40579      * @return {Roo.TabPanelItem} The TabPanelItem.
40580      */
40581     activate : function(id)
40582     {
40583         //Roo.log('activite:'  + id);
40584         
40585         var tab = this.items[id];
40586         if(!tab){
40587             return null;
40588         }
40589         if(tab == this.active || tab.disabled){
40590             return tab;
40591         }
40592         var e = {};
40593         this.fireEvent("beforetabchange", this, e, tab);
40594         if(e.cancel !== true && !tab.disabled){
40595             if(this.active){
40596                 this.active.hide();
40597             }
40598             this.active = this.items[id];
40599             this.active.show();
40600             this.fireEvent("tabchange", this, this.active);
40601         }
40602         return tab;
40603     },
40604
40605     /**
40606      * Gets the active {@link Roo.TabPanelItem}.
40607      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40608      */
40609     getActiveTab : function(){
40610         return this.active;
40611     },
40612
40613     /**
40614      * Updates the tab body element to fit the height of the container element
40615      * for overflow scrolling
40616      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40617      */
40618     syncHeight : function(targetHeight){
40619         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40620         var bm = this.bodyEl.getMargins();
40621         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40622         this.bodyEl.setHeight(newHeight);
40623         return newHeight;
40624     },
40625
40626     onResize : function(){
40627         if(this.monitorResize){
40628             this.autoSizeTabs();
40629         }
40630     },
40631
40632     /**
40633      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40634      */
40635     beginUpdate : function(){
40636         this.updating = true;
40637     },
40638
40639     /**
40640      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40641      */
40642     endUpdate : function(){
40643         this.updating = false;
40644         this.autoSizeTabs();
40645     },
40646
40647     /**
40648      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40649      */
40650     autoSizeTabs : function()
40651     {
40652         var count = this.items.length;
40653         var vcount = count - this.hiddenCount;
40654         
40655         if (vcount < 2) {
40656             this.stripEl.hide();
40657         } else {
40658             this.stripEl.show();
40659         }
40660         
40661         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40662             return;
40663         }
40664         
40665         
40666         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40667         var availWidth = Math.floor(w / vcount);
40668         var b = this.stripBody;
40669         if(b.getWidth() > w){
40670             var tabs = this.items;
40671             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40672             if(availWidth < this.minTabWidth){
40673                 /*if(!this.sleft){    // incomplete scrolling code
40674                     this.createScrollButtons();
40675                 }
40676                 this.showScroll();
40677                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40678             }
40679         }else{
40680             if(this.currentTabWidth < this.preferredTabWidth){
40681                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40682             }
40683         }
40684     },
40685
40686     /**
40687      * Returns the number of tabs in this TabPanel.
40688      * @return {Number}
40689      */
40690      getCount : function(){
40691          return this.items.length;
40692      },
40693
40694     /**
40695      * Resizes all the tabs to the passed width
40696      * @param {Number} The new width
40697      */
40698     setTabWidth : function(width){
40699         this.currentTabWidth = width;
40700         for(var i = 0, len = this.items.length; i < len; i++) {
40701                 if(!this.items[i].isHidden()) {
40702                 this.items[i].setWidth(width);
40703             }
40704         }
40705     },
40706
40707     /**
40708      * Destroys this TabPanel
40709      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40710      */
40711     destroy : function(removeEl){
40712         Roo.EventManager.removeResizeListener(this.onResize, this);
40713         for(var i = 0, len = this.items.length; i < len; i++){
40714             this.items[i].purgeListeners();
40715         }
40716         if(removeEl === true){
40717             this.el.update("");
40718             this.el.remove();
40719         }
40720     },
40721     
40722     createStrip : function(container)
40723     {
40724         var strip = document.createElement("nav");
40725         strip.className = Roo.bootstrap.version == 4 ?
40726             "navbar-light bg-light" : 
40727             "navbar navbar-default"; //"x-tabs-wrap";
40728         container.appendChild(strip);
40729         return strip;
40730     },
40731     
40732     createStripList : function(strip)
40733     {
40734         // div wrapper for retard IE
40735         // returns the "tr" element.
40736         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40737         //'<div class="x-tabs-strip-wrap">'+
40738           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40739           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40740         return strip.firstChild; //.firstChild.firstChild.firstChild;
40741     },
40742     createBody : function(container)
40743     {
40744         var body = document.createElement("div");
40745         Roo.id(body, "tab-body");
40746         //Roo.fly(body).addClass("x-tabs-body");
40747         Roo.fly(body).addClass("tab-content");
40748         container.appendChild(body);
40749         return body;
40750     },
40751     createItemBody :function(bodyEl, id){
40752         var body = Roo.getDom(id);
40753         if(!body){
40754             body = document.createElement("div");
40755             body.id = id;
40756         }
40757         //Roo.fly(body).addClass("x-tabs-item-body");
40758         Roo.fly(body).addClass("tab-pane");
40759          bodyEl.insertBefore(body, bodyEl.firstChild);
40760         return body;
40761     },
40762     /** @private */
40763     createStripElements :  function(stripEl, text, closable, tpl)
40764     {
40765         var td = document.createElement("li"); // was td..
40766         td.className = 'nav-item';
40767         
40768         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40769         
40770         
40771         stripEl.appendChild(td);
40772         /*if(closable){
40773             td.className = "x-tabs-closable";
40774             if(!this.closeTpl){
40775                 this.closeTpl = new Roo.Template(
40776                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40777                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40778                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40779                 );
40780             }
40781             var el = this.closeTpl.overwrite(td, {"text": text});
40782             var close = el.getElementsByTagName("div")[0];
40783             var inner = el.getElementsByTagName("em")[0];
40784             return {"el": el, "close": close, "inner": inner};
40785         } else {
40786         */
40787         // not sure what this is..
40788 //            if(!this.tabTpl){
40789                 //this.tabTpl = new Roo.Template(
40790                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40791                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40792                 //);
40793 //                this.tabTpl = new Roo.Template(
40794 //                   '<a href="#">' +
40795 //                   '<span unselectable="on"' +
40796 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40797 //                            ' >{text}</span></a>'
40798 //                );
40799 //                
40800 //            }
40801
40802
40803             var template = tpl || this.tabTpl || false;
40804             
40805             if(!template){
40806                 template =  new Roo.Template(
40807                         Roo.bootstrap.version == 4 ? 
40808                             (
40809                                 '<a class="nav-link" href="#" unselectable="on"' +
40810                                      (this.disableTooltips ? '' : ' title="{text}"') +
40811                                      ' >{text}</a>'
40812                             ) : (
40813                                 '<a class="nav-link" href="#">' +
40814                                 '<span unselectable="on"' +
40815                                          (this.disableTooltips ? '' : ' title="{text}"') +
40816                                     ' >{text}</span></a>'
40817                             )
40818                 );
40819             }
40820             
40821             switch (typeof(template)) {
40822                 case 'object' :
40823                     break;
40824                 case 'string' :
40825                     template = new Roo.Template(template);
40826                     break;
40827                 default :
40828                     break;
40829             }
40830             
40831             var el = template.overwrite(td, {"text": text});
40832             
40833             var inner = el.getElementsByTagName("span")[0];
40834             
40835             return {"el": el, "inner": inner};
40836             
40837     }
40838         
40839     
40840 });
40841
40842 /**
40843  * @class Roo.TabPanelItem
40844  * @extends Roo.util.Observable
40845  * Represents an individual item (tab plus body) in a TabPanel.
40846  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40847  * @param {String} id The id of this TabPanelItem
40848  * @param {String} text The text for the tab of this TabPanelItem
40849  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40850  */
40851 Roo.bootstrap.panel.TabItem = function(config){
40852     /**
40853      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40854      * @type Roo.TabPanel
40855      */
40856     this.tabPanel = config.panel;
40857     /**
40858      * The id for this TabPanelItem
40859      * @type String
40860      */
40861     this.id = config.id;
40862     /** @private */
40863     this.disabled = false;
40864     /** @private */
40865     this.text = config.text;
40866     /** @private */
40867     this.loaded = false;
40868     this.closable = config.closable;
40869
40870     /**
40871      * The body element for this TabPanelItem.
40872      * @type Roo.Element
40873      */
40874     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40875     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40876     this.bodyEl.setStyle("display", "block");
40877     this.bodyEl.setStyle("zoom", "1");
40878     //this.hideAction();
40879
40880     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40881     /** @private */
40882     this.el = Roo.get(els.el);
40883     this.inner = Roo.get(els.inner, true);
40884      this.textEl = Roo.bootstrap.version == 4 ?
40885         this.el : Roo.get(this.el.dom.firstChild, true);
40886
40887     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40888     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40889
40890     
40891 //    this.el.on("mousedown", this.onTabMouseDown, this);
40892     this.el.on("click", this.onTabClick, this);
40893     /** @private */
40894     if(config.closable){
40895         var c = Roo.get(els.close, true);
40896         c.dom.title = this.closeText;
40897         c.addClassOnOver("close-over");
40898         c.on("click", this.closeClick, this);
40899      }
40900
40901     this.addEvents({
40902          /**
40903          * @event activate
40904          * Fires when this tab becomes the active tab.
40905          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40906          * @param {Roo.TabPanelItem} this
40907          */
40908         "activate": true,
40909         /**
40910          * @event beforeclose
40911          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40912          * @param {Roo.TabPanelItem} this
40913          * @param {Object} e Set cancel to true on this object to cancel the close.
40914          */
40915         "beforeclose": true,
40916         /**
40917          * @event close
40918          * Fires when this tab is closed.
40919          * @param {Roo.TabPanelItem} this
40920          */
40921          "close": true,
40922         /**
40923          * @event deactivate
40924          * Fires when this tab is no longer the active tab.
40925          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40926          * @param {Roo.TabPanelItem} this
40927          */
40928          "deactivate" : true
40929     });
40930     this.hidden = false;
40931
40932     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40933 };
40934
40935 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40936            {
40937     purgeListeners : function(){
40938        Roo.util.Observable.prototype.purgeListeners.call(this);
40939        this.el.removeAllListeners();
40940     },
40941     /**
40942      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40943      */
40944     show : function(){
40945         this.status_node.addClass("active");
40946         this.showAction();
40947         if(Roo.isOpera){
40948             this.tabPanel.stripWrap.repaint();
40949         }
40950         this.fireEvent("activate", this.tabPanel, this);
40951     },
40952
40953     /**
40954      * Returns true if this tab is the active tab.
40955      * @return {Boolean}
40956      */
40957     isActive : function(){
40958         return this.tabPanel.getActiveTab() == this;
40959     },
40960
40961     /**
40962      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40963      */
40964     hide : function(){
40965         this.status_node.removeClass("active");
40966         this.hideAction();
40967         this.fireEvent("deactivate", this.tabPanel, this);
40968     },
40969
40970     hideAction : function(){
40971         this.bodyEl.hide();
40972         this.bodyEl.setStyle("position", "absolute");
40973         this.bodyEl.setLeft("-20000px");
40974         this.bodyEl.setTop("-20000px");
40975     },
40976
40977     showAction : function(){
40978         this.bodyEl.setStyle("position", "relative");
40979         this.bodyEl.setTop("");
40980         this.bodyEl.setLeft("");
40981         this.bodyEl.show();
40982     },
40983
40984     /**
40985      * Set the tooltip for the tab.
40986      * @param {String} tooltip The tab's tooltip
40987      */
40988     setTooltip : function(text){
40989         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40990             this.textEl.dom.qtip = text;
40991             this.textEl.dom.removeAttribute('title');
40992         }else{
40993             this.textEl.dom.title = text;
40994         }
40995     },
40996
40997     onTabClick : function(e){
40998         e.preventDefault();
40999         this.tabPanel.activate(this.id);
41000     },
41001
41002     onTabMouseDown : function(e){
41003         e.preventDefault();
41004         this.tabPanel.activate(this.id);
41005     },
41006 /*
41007     getWidth : function(){
41008         return this.inner.getWidth();
41009     },
41010
41011     setWidth : function(width){
41012         var iwidth = width - this.linode.getPadding("lr");
41013         this.inner.setWidth(iwidth);
41014         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41015         this.linode.setWidth(width);
41016     },
41017 */
41018     /**
41019      * Show or hide the tab
41020      * @param {Boolean} hidden True to hide or false to show.
41021      */
41022     setHidden : function(hidden){
41023         this.hidden = hidden;
41024         this.linode.setStyle("display", hidden ? "none" : "");
41025     },
41026
41027     /**
41028      * Returns true if this tab is "hidden"
41029      * @return {Boolean}
41030      */
41031     isHidden : function(){
41032         return this.hidden;
41033     },
41034
41035     /**
41036      * Returns the text for this tab
41037      * @return {String}
41038      */
41039     getText : function(){
41040         return this.text;
41041     },
41042     /*
41043     autoSize : function(){
41044         //this.el.beginMeasure();
41045         this.textEl.setWidth(1);
41046         /*
41047          *  #2804 [new] Tabs in Roojs
41048          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41049          */
41050         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41051         //this.el.endMeasure();
41052     //},
41053
41054     /**
41055      * Sets the text for the tab (Note: this also sets the tooltip text)
41056      * @param {String} text The tab's text and tooltip
41057      */
41058     setText : function(text){
41059         this.text = text;
41060         this.textEl.update(text);
41061         this.setTooltip(text);
41062         //if(!this.tabPanel.resizeTabs){
41063         //    this.autoSize();
41064         //}
41065     },
41066     /**
41067      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41068      */
41069     activate : function(){
41070         this.tabPanel.activate(this.id);
41071     },
41072
41073     /**
41074      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41075      */
41076     disable : function(){
41077         if(this.tabPanel.active != this){
41078             this.disabled = true;
41079             this.status_node.addClass("disabled");
41080         }
41081     },
41082
41083     /**
41084      * Enables this TabPanelItem if it was previously disabled.
41085      */
41086     enable : function(){
41087         this.disabled = false;
41088         this.status_node.removeClass("disabled");
41089     },
41090
41091     /**
41092      * Sets the content for this TabPanelItem.
41093      * @param {String} content The content
41094      * @param {Boolean} loadScripts true to look for and load scripts
41095      */
41096     setContent : function(content, loadScripts){
41097         this.bodyEl.update(content, loadScripts);
41098     },
41099
41100     /**
41101      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41102      * @return {Roo.UpdateManager} The UpdateManager
41103      */
41104     getUpdateManager : function(){
41105         return this.bodyEl.getUpdateManager();
41106     },
41107
41108     /**
41109      * Set a URL to be used to load the content for this TabPanelItem.
41110      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41111      * @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)
41112      * @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)
41113      * @return {Roo.UpdateManager} The UpdateManager
41114      */
41115     setUrl : function(url, params, loadOnce){
41116         if(this.refreshDelegate){
41117             this.un('activate', this.refreshDelegate);
41118         }
41119         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41120         this.on("activate", this.refreshDelegate);
41121         return this.bodyEl.getUpdateManager();
41122     },
41123
41124     /** @private */
41125     _handleRefresh : function(url, params, loadOnce){
41126         if(!loadOnce || !this.loaded){
41127             var updater = this.bodyEl.getUpdateManager();
41128             updater.update(url, params, this._setLoaded.createDelegate(this));
41129         }
41130     },
41131
41132     /**
41133      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41134      *   Will fail silently if the setUrl method has not been called.
41135      *   This does not activate the panel, just updates its content.
41136      */
41137     refresh : function(){
41138         if(this.refreshDelegate){
41139            this.loaded = false;
41140            this.refreshDelegate();
41141         }
41142     },
41143
41144     /** @private */
41145     _setLoaded : function(){
41146         this.loaded = true;
41147     },
41148
41149     /** @private */
41150     closeClick : function(e){
41151         var o = {};
41152         e.stopEvent();
41153         this.fireEvent("beforeclose", this, o);
41154         if(o.cancel !== true){
41155             this.tabPanel.removeTab(this.id);
41156         }
41157     },
41158     /**
41159      * The text displayed in the tooltip for the close icon.
41160      * @type String
41161      */
41162     closeText : "Close this tab"
41163 });
41164 /**
41165 *    This script refer to:
41166 *    Title: International Telephone Input
41167 *    Author: Jack O'Connor
41168 *    Code version:  v12.1.12
41169 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41170 **/
41171
41172 Roo.bootstrap.PhoneInputData = function() {
41173     var d = [
41174       [
41175         "Afghanistan (‫افغانستان‬‎)",
41176         "af",
41177         "93"
41178       ],
41179       [
41180         "Albania (Shqipëri)",
41181         "al",
41182         "355"
41183       ],
41184       [
41185         "Algeria (‫الجزائر‬‎)",
41186         "dz",
41187         "213"
41188       ],
41189       [
41190         "American Samoa",
41191         "as",
41192         "1684"
41193       ],
41194       [
41195         "Andorra",
41196         "ad",
41197         "376"
41198       ],
41199       [
41200         "Angola",
41201         "ao",
41202         "244"
41203       ],
41204       [
41205         "Anguilla",
41206         "ai",
41207         "1264"
41208       ],
41209       [
41210         "Antigua and Barbuda",
41211         "ag",
41212         "1268"
41213       ],
41214       [
41215         "Argentina",
41216         "ar",
41217         "54"
41218       ],
41219       [
41220         "Armenia (Հայաստան)",
41221         "am",
41222         "374"
41223       ],
41224       [
41225         "Aruba",
41226         "aw",
41227         "297"
41228       ],
41229       [
41230         "Australia",
41231         "au",
41232         "61",
41233         0
41234       ],
41235       [
41236         "Austria (Österreich)",
41237         "at",
41238         "43"
41239       ],
41240       [
41241         "Azerbaijan (Azərbaycan)",
41242         "az",
41243         "994"
41244       ],
41245       [
41246         "Bahamas",
41247         "bs",
41248         "1242"
41249       ],
41250       [
41251         "Bahrain (‫البحرين‬‎)",
41252         "bh",
41253         "973"
41254       ],
41255       [
41256         "Bangladesh (বাংলাদেশ)",
41257         "bd",
41258         "880"
41259       ],
41260       [
41261         "Barbados",
41262         "bb",
41263         "1246"
41264       ],
41265       [
41266         "Belarus (Беларусь)",
41267         "by",
41268         "375"
41269       ],
41270       [
41271         "Belgium (België)",
41272         "be",
41273         "32"
41274       ],
41275       [
41276         "Belize",
41277         "bz",
41278         "501"
41279       ],
41280       [
41281         "Benin (Bénin)",
41282         "bj",
41283         "229"
41284       ],
41285       [
41286         "Bermuda",
41287         "bm",
41288         "1441"
41289       ],
41290       [
41291         "Bhutan (འབྲུག)",
41292         "bt",
41293         "975"
41294       ],
41295       [
41296         "Bolivia",
41297         "bo",
41298         "591"
41299       ],
41300       [
41301         "Bosnia and Herzegovina (Босна и Херцеговина)",
41302         "ba",
41303         "387"
41304       ],
41305       [
41306         "Botswana",
41307         "bw",
41308         "267"
41309       ],
41310       [
41311         "Brazil (Brasil)",
41312         "br",
41313         "55"
41314       ],
41315       [
41316         "British Indian Ocean Territory",
41317         "io",
41318         "246"
41319       ],
41320       [
41321         "British Virgin Islands",
41322         "vg",
41323         "1284"
41324       ],
41325       [
41326         "Brunei",
41327         "bn",
41328         "673"
41329       ],
41330       [
41331         "Bulgaria (България)",
41332         "bg",
41333         "359"
41334       ],
41335       [
41336         "Burkina Faso",
41337         "bf",
41338         "226"
41339       ],
41340       [
41341         "Burundi (Uburundi)",
41342         "bi",
41343         "257"
41344       ],
41345       [
41346         "Cambodia (កម្ពុជា)",
41347         "kh",
41348         "855"
41349       ],
41350       [
41351         "Cameroon (Cameroun)",
41352         "cm",
41353         "237"
41354       ],
41355       [
41356         "Canada",
41357         "ca",
41358         "1",
41359         1,
41360         ["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"]
41361       ],
41362       [
41363         "Cape Verde (Kabu Verdi)",
41364         "cv",
41365         "238"
41366       ],
41367       [
41368         "Caribbean Netherlands",
41369         "bq",
41370         "599",
41371         1
41372       ],
41373       [
41374         "Cayman Islands",
41375         "ky",
41376         "1345"
41377       ],
41378       [
41379         "Central African Republic (République centrafricaine)",
41380         "cf",
41381         "236"
41382       ],
41383       [
41384         "Chad (Tchad)",
41385         "td",
41386         "235"
41387       ],
41388       [
41389         "Chile",
41390         "cl",
41391         "56"
41392       ],
41393       [
41394         "China (中国)",
41395         "cn",
41396         "86"
41397       ],
41398       [
41399         "Christmas Island",
41400         "cx",
41401         "61",
41402         2
41403       ],
41404       [
41405         "Cocos (Keeling) Islands",
41406         "cc",
41407         "61",
41408         1
41409       ],
41410       [
41411         "Colombia",
41412         "co",
41413         "57"
41414       ],
41415       [
41416         "Comoros (‫جزر القمر‬‎)",
41417         "km",
41418         "269"
41419       ],
41420       [
41421         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41422         "cd",
41423         "243"
41424       ],
41425       [
41426         "Congo (Republic) (Congo-Brazzaville)",
41427         "cg",
41428         "242"
41429       ],
41430       [
41431         "Cook Islands",
41432         "ck",
41433         "682"
41434       ],
41435       [
41436         "Costa Rica",
41437         "cr",
41438         "506"
41439       ],
41440       [
41441         "Côte d’Ivoire",
41442         "ci",
41443         "225"
41444       ],
41445       [
41446         "Croatia (Hrvatska)",
41447         "hr",
41448         "385"
41449       ],
41450       [
41451         "Cuba",
41452         "cu",
41453         "53"
41454       ],
41455       [
41456         "Curaçao",
41457         "cw",
41458         "599",
41459         0
41460       ],
41461       [
41462         "Cyprus (Κύπρος)",
41463         "cy",
41464         "357"
41465       ],
41466       [
41467         "Czech Republic (Česká republika)",
41468         "cz",
41469         "420"
41470       ],
41471       [
41472         "Denmark (Danmark)",
41473         "dk",
41474         "45"
41475       ],
41476       [
41477         "Djibouti",
41478         "dj",
41479         "253"
41480       ],
41481       [
41482         "Dominica",
41483         "dm",
41484         "1767"
41485       ],
41486       [
41487         "Dominican Republic (República Dominicana)",
41488         "do",
41489         "1",
41490         2,
41491         ["809", "829", "849"]
41492       ],
41493       [
41494         "Ecuador",
41495         "ec",
41496         "593"
41497       ],
41498       [
41499         "Egypt (‫مصر‬‎)",
41500         "eg",
41501         "20"
41502       ],
41503       [
41504         "El Salvador",
41505         "sv",
41506         "503"
41507       ],
41508       [
41509         "Equatorial Guinea (Guinea Ecuatorial)",
41510         "gq",
41511         "240"
41512       ],
41513       [
41514         "Eritrea",
41515         "er",
41516         "291"
41517       ],
41518       [
41519         "Estonia (Eesti)",
41520         "ee",
41521         "372"
41522       ],
41523       [
41524         "Ethiopia",
41525         "et",
41526         "251"
41527       ],
41528       [
41529         "Falkland Islands (Islas Malvinas)",
41530         "fk",
41531         "500"
41532       ],
41533       [
41534         "Faroe Islands (Føroyar)",
41535         "fo",
41536         "298"
41537       ],
41538       [
41539         "Fiji",
41540         "fj",
41541         "679"
41542       ],
41543       [
41544         "Finland (Suomi)",
41545         "fi",
41546         "358",
41547         0
41548       ],
41549       [
41550         "France",
41551         "fr",
41552         "33"
41553       ],
41554       [
41555         "French Guiana (Guyane française)",
41556         "gf",
41557         "594"
41558       ],
41559       [
41560         "French Polynesia (Polynésie française)",
41561         "pf",
41562         "689"
41563       ],
41564       [
41565         "Gabon",
41566         "ga",
41567         "241"
41568       ],
41569       [
41570         "Gambia",
41571         "gm",
41572         "220"
41573       ],
41574       [
41575         "Georgia (საქართველო)",
41576         "ge",
41577         "995"
41578       ],
41579       [
41580         "Germany (Deutschland)",
41581         "de",
41582         "49"
41583       ],
41584       [
41585         "Ghana (Gaana)",
41586         "gh",
41587         "233"
41588       ],
41589       [
41590         "Gibraltar",
41591         "gi",
41592         "350"
41593       ],
41594       [
41595         "Greece (Ελλάδα)",
41596         "gr",
41597         "30"
41598       ],
41599       [
41600         "Greenland (Kalaallit Nunaat)",
41601         "gl",
41602         "299"
41603       ],
41604       [
41605         "Grenada",
41606         "gd",
41607         "1473"
41608       ],
41609       [
41610         "Guadeloupe",
41611         "gp",
41612         "590",
41613         0
41614       ],
41615       [
41616         "Guam",
41617         "gu",
41618         "1671"
41619       ],
41620       [
41621         "Guatemala",
41622         "gt",
41623         "502"
41624       ],
41625       [
41626         "Guernsey",
41627         "gg",
41628         "44",
41629         1
41630       ],
41631       [
41632         "Guinea (Guinée)",
41633         "gn",
41634         "224"
41635       ],
41636       [
41637         "Guinea-Bissau (Guiné Bissau)",
41638         "gw",
41639         "245"
41640       ],
41641       [
41642         "Guyana",
41643         "gy",
41644         "592"
41645       ],
41646       [
41647         "Haiti",
41648         "ht",
41649         "509"
41650       ],
41651       [
41652         "Honduras",
41653         "hn",
41654         "504"
41655       ],
41656       [
41657         "Hong Kong (香港)",
41658         "hk",
41659         "852"
41660       ],
41661       [
41662         "Hungary (Magyarország)",
41663         "hu",
41664         "36"
41665       ],
41666       [
41667         "Iceland (Ísland)",
41668         "is",
41669         "354"
41670       ],
41671       [
41672         "India (भारत)",
41673         "in",
41674         "91"
41675       ],
41676       [
41677         "Indonesia",
41678         "id",
41679         "62"
41680       ],
41681       [
41682         "Iran (‫ایران‬‎)",
41683         "ir",
41684         "98"
41685       ],
41686       [
41687         "Iraq (‫العراق‬‎)",
41688         "iq",
41689         "964"
41690       ],
41691       [
41692         "Ireland",
41693         "ie",
41694         "353"
41695       ],
41696       [
41697         "Isle of Man",
41698         "im",
41699         "44",
41700         2
41701       ],
41702       [
41703         "Israel (‫ישראל‬‎)",
41704         "il",
41705         "972"
41706       ],
41707       [
41708         "Italy (Italia)",
41709         "it",
41710         "39",
41711         0
41712       ],
41713       [
41714         "Jamaica",
41715         "jm",
41716         "1876"
41717       ],
41718       [
41719         "Japan (日本)",
41720         "jp",
41721         "81"
41722       ],
41723       [
41724         "Jersey",
41725         "je",
41726         "44",
41727         3
41728       ],
41729       [
41730         "Jordan (‫الأردن‬‎)",
41731         "jo",
41732         "962"
41733       ],
41734       [
41735         "Kazakhstan (Казахстан)",
41736         "kz",
41737         "7",
41738         1
41739       ],
41740       [
41741         "Kenya",
41742         "ke",
41743         "254"
41744       ],
41745       [
41746         "Kiribati",
41747         "ki",
41748         "686"
41749       ],
41750       [
41751         "Kosovo",
41752         "xk",
41753         "383"
41754       ],
41755       [
41756         "Kuwait (‫الكويت‬‎)",
41757         "kw",
41758         "965"
41759       ],
41760       [
41761         "Kyrgyzstan (Кыргызстан)",
41762         "kg",
41763         "996"
41764       ],
41765       [
41766         "Laos (ລາວ)",
41767         "la",
41768         "856"
41769       ],
41770       [
41771         "Latvia (Latvija)",
41772         "lv",
41773         "371"
41774       ],
41775       [
41776         "Lebanon (‫لبنان‬‎)",
41777         "lb",
41778         "961"
41779       ],
41780       [
41781         "Lesotho",
41782         "ls",
41783         "266"
41784       ],
41785       [
41786         "Liberia",
41787         "lr",
41788         "231"
41789       ],
41790       [
41791         "Libya (‫ليبيا‬‎)",
41792         "ly",
41793         "218"
41794       ],
41795       [
41796         "Liechtenstein",
41797         "li",
41798         "423"
41799       ],
41800       [
41801         "Lithuania (Lietuva)",
41802         "lt",
41803         "370"
41804       ],
41805       [
41806         "Luxembourg",
41807         "lu",
41808         "352"
41809       ],
41810       [
41811         "Macau (澳門)",
41812         "mo",
41813         "853"
41814       ],
41815       [
41816         "Macedonia (FYROM) (Македонија)",
41817         "mk",
41818         "389"
41819       ],
41820       [
41821         "Madagascar (Madagasikara)",
41822         "mg",
41823         "261"
41824       ],
41825       [
41826         "Malawi",
41827         "mw",
41828         "265"
41829       ],
41830       [
41831         "Malaysia",
41832         "my",
41833         "60"
41834       ],
41835       [
41836         "Maldives",
41837         "mv",
41838         "960"
41839       ],
41840       [
41841         "Mali",
41842         "ml",
41843         "223"
41844       ],
41845       [
41846         "Malta",
41847         "mt",
41848         "356"
41849       ],
41850       [
41851         "Marshall Islands",
41852         "mh",
41853         "692"
41854       ],
41855       [
41856         "Martinique",
41857         "mq",
41858         "596"
41859       ],
41860       [
41861         "Mauritania (‫موريتانيا‬‎)",
41862         "mr",
41863         "222"
41864       ],
41865       [
41866         "Mauritius (Moris)",
41867         "mu",
41868         "230"
41869       ],
41870       [
41871         "Mayotte",
41872         "yt",
41873         "262",
41874         1
41875       ],
41876       [
41877         "Mexico (México)",
41878         "mx",
41879         "52"
41880       ],
41881       [
41882         "Micronesia",
41883         "fm",
41884         "691"
41885       ],
41886       [
41887         "Moldova (Republica Moldova)",
41888         "md",
41889         "373"
41890       ],
41891       [
41892         "Monaco",
41893         "mc",
41894         "377"
41895       ],
41896       [
41897         "Mongolia (Монгол)",
41898         "mn",
41899         "976"
41900       ],
41901       [
41902         "Montenegro (Crna Gora)",
41903         "me",
41904         "382"
41905       ],
41906       [
41907         "Montserrat",
41908         "ms",
41909         "1664"
41910       ],
41911       [
41912         "Morocco (‫المغرب‬‎)",
41913         "ma",
41914         "212",
41915         0
41916       ],
41917       [
41918         "Mozambique (Moçambique)",
41919         "mz",
41920         "258"
41921       ],
41922       [
41923         "Myanmar (Burma) (မြန်မာ)",
41924         "mm",
41925         "95"
41926       ],
41927       [
41928         "Namibia (Namibië)",
41929         "na",
41930         "264"
41931       ],
41932       [
41933         "Nauru",
41934         "nr",
41935         "674"
41936       ],
41937       [
41938         "Nepal (नेपाल)",
41939         "np",
41940         "977"
41941       ],
41942       [
41943         "Netherlands (Nederland)",
41944         "nl",
41945         "31"
41946       ],
41947       [
41948         "New Caledonia (Nouvelle-Calédonie)",
41949         "nc",
41950         "687"
41951       ],
41952       [
41953         "New Zealand",
41954         "nz",
41955         "64"
41956       ],
41957       [
41958         "Nicaragua",
41959         "ni",
41960         "505"
41961       ],
41962       [
41963         "Niger (Nijar)",
41964         "ne",
41965         "227"
41966       ],
41967       [
41968         "Nigeria",
41969         "ng",
41970         "234"
41971       ],
41972       [
41973         "Niue",
41974         "nu",
41975         "683"
41976       ],
41977       [
41978         "Norfolk Island",
41979         "nf",
41980         "672"
41981       ],
41982       [
41983         "North Korea (조선 민주주의 인민 공화국)",
41984         "kp",
41985         "850"
41986       ],
41987       [
41988         "Northern Mariana Islands",
41989         "mp",
41990         "1670"
41991       ],
41992       [
41993         "Norway (Norge)",
41994         "no",
41995         "47",
41996         0
41997       ],
41998       [
41999         "Oman (‫عُمان‬‎)",
42000         "om",
42001         "968"
42002       ],
42003       [
42004         "Pakistan (‫پاکستان‬‎)",
42005         "pk",
42006         "92"
42007       ],
42008       [
42009         "Palau",
42010         "pw",
42011         "680"
42012       ],
42013       [
42014         "Palestine (‫فلسطين‬‎)",
42015         "ps",
42016         "970"
42017       ],
42018       [
42019         "Panama (Panamá)",
42020         "pa",
42021         "507"
42022       ],
42023       [
42024         "Papua New Guinea",
42025         "pg",
42026         "675"
42027       ],
42028       [
42029         "Paraguay",
42030         "py",
42031         "595"
42032       ],
42033       [
42034         "Peru (Perú)",
42035         "pe",
42036         "51"
42037       ],
42038       [
42039         "Philippines",
42040         "ph",
42041         "63"
42042       ],
42043       [
42044         "Poland (Polska)",
42045         "pl",
42046         "48"
42047       ],
42048       [
42049         "Portugal",
42050         "pt",
42051         "351"
42052       ],
42053       [
42054         "Puerto Rico",
42055         "pr",
42056         "1",
42057         3,
42058         ["787", "939"]
42059       ],
42060       [
42061         "Qatar (‫قطر‬‎)",
42062         "qa",
42063         "974"
42064       ],
42065       [
42066         "Réunion (La Réunion)",
42067         "re",
42068         "262",
42069         0
42070       ],
42071       [
42072         "Romania (România)",
42073         "ro",
42074         "40"
42075       ],
42076       [
42077         "Russia (Россия)",
42078         "ru",
42079         "7",
42080         0
42081       ],
42082       [
42083         "Rwanda",
42084         "rw",
42085         "250"
42086       ],
42087       [
42088         "Saint Barthélemy",
42089         "bl",
42090         "590",
42091         1
42092       ],
42093       [
42094         "Saint Helena",
42095         "sh",
42096         "290"
42097       ],
42098       [
42099         "Saint Kitts and Nevis",
42100         "kn",
42101         "1869"
42102       ],
42103       [
42104         "Saint Lucia",
42105         "lc",
42106         "1758"
42107       ],
42108       [
42109         "Saint Martin (Saint-Martin (partie française))",
42110         "mf",
42111         "590",
42112         2
42113       ],
42114       [
42115         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42116         "pm",
42117         "508"
42118       ],
42119       [
42120         "Saint Vincent and the Grenadines",
42121         "vc",
42122         "1784"
42123       ],
42124       [
42125         "Samoa",
42126         "ws",
42127         "685"
42128       ],
42129       [
42130         "San Marino",
42131         "sm",
42132         "378"
42133       ],
42134       [
42135         "São Tomé and Príncipe (São Tomé e Príncipe)",
42136         "st",
42137         "239"
42138       ],
42139       [
42140         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42141         "sa",
42142         "966"
42143       ],
42144       [
42145         "Senegal (Sénégal)",
42146         "sn",
42147         "221"
42148       ],
42149       [
42150         "Serbia (Србија)",
42151         "rs",
42152         "381"
42153       ],
42154       [
42155         "Seychelles",
42156         "sc",
42157         "248"
42158       ],
42159       [
42160         "Sierra Leone",
42161         "sl",
42162         "232"
42163       ],
42164       [
42165         "Singapore",
42166         "sg",
42167         "65"
42168       ],
42169       [
42170         "Sint Maarten",
42171         "sx",
42172         "1721"
42173       ],
42174       [
42175         "Slovakia (Slovensko)",
42176         "sk",
42177         "421"
42178       ],
42179       [
42180         "Slovenia (Slovenija)",
42181         "si",
42182         "386"
42183       ],
42184       [
42185         "Solomon Islands",
42186         "sb",
42187         "677"
42188       ],
42189       [
42190         "Somalia (Soomaaliya)",
42191         "so",
42192         "252"
42193       ],
42194       [
42195         "South Africa",
42196         "za",
42197         "27"
42198       ],
42199       [
42200         "South Korea (대한민국)",
42201         "kr",
42202         "82"
42203       ],
42204       [
42205         "South Sudan (‫جنوب السودان‬‎)",
42206         "ss",
42207         "211"
42208       ],
42209       [
42210         "Spain (España)",
42211         "es",
42212         "34"
42213       ],
42214       [
42215         "Sri Lanka (ශ්‍රී ලංකාව)",
42216         "lk",
42217         "94"
42218       ],
42219       [
42220         "Sudan (‫السودان‬‎)",
42221         "sd",
42222         "249"
42223       ],
42224       [
42225         "Suriname",
42226         "sr",
42227         "597"
42228       ],
42229       [
42230         "Svalbard and Jan Mayen",
42231         "sj",
42232         "47",
42233         1
42234       ],
42235       [
42236         "Swaziland",
42237         "sz",
42238         "268"
42239       ],
42240       [
42241         "Sweden (Sverige)",
42242         "se",
42243         "46"
42244       ],
42245       [
42246         "Switzerland (Schweiz)",
42247         "ch",
42248         "41"
42249       ],
42250       [
42251         "Syria (‫سوريا‬‎)",
42252         "sy",
42253         "963"
42254       ],
42255       [
42256         "Taiwan (台灣)",
42257         "tw",
42258         "886"
42259       ],
42260       [
42261         "Tajikistan",
42262         "tj",
42263         "992"
42264       ],
42265       [
42266         "Tanzania",
42267         "tz",
42268         "255"
42269       ],
42270       [
42271         "Thailand (ไทย)",
42272         "th",
42273         "66"
42274       ],
42275       [
42276         "Timor-Leste",
42277         "tl",
42278         "670"
42279       ],
42280       [
42281         "Togo",
42282         "tg",
42283         "228"
42284       ],
42285       [
42286         "Tokelau",
42287         "tk",
42288         "690"
42289       ],
42290       [
42291         "Tonga",
42292         "to",
42293         "676"
42294       ],
42295       [
42296         "Trinidad and Tobago",
42297         "tt",
42298         "1868"
42299       ],
42300       [
42301         "Tunisia (‫تونس‬‎)",
42302         "tn",
42303         "216"
42304       ],
42305       [
42306         "Turkey (Türkiye)",
42307         "tr",
42308         "90"
42309       ],
42310       [
42311         "Turkmenistan",
42312         "tm",
42313         "993"
42314       ],
42315       [
42316         "Turks and Caicos Islands",
42317         "tc",
42318         "1649"
42319       ],
42320       [
42321         "Tuvalu",
42322         "tv",
42323         "688"
42324       ],
42325       [
42326         "U.S. Virgin Islands",
42327         "vi",
42328         "1340"
42329       ],
42330       [
42331         "Uganda",
42332         "ug",
42333         "256"
42334       ],
42335       [
42336         "Ukraine (Україна)",
42337         "ua",
42338         "380"
42339       ],
42340       [
42341         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42342         "ae",
42343         "971"
42344       ],
42345       [
42346         "United Kingdom",
42347         "gb",
42348         "44",
42349         0
42350       ],
42351       [
42352         "United States",
42353         "us",
42354         "1",
42355         0
42356       ],
42357       [
42358         "Uruguay",
42359         "uy",
42360         "598"
42361       ],
42362       [
42363         "Uzbekistan (Oʻzbekiston)",
42364         "uz",
42365         "998"
42366       ],
42367       [
42368         "Vanuatu",
42369         "vu",
42370         "678"
42371       ],
42372       [
42373         "Vatican City (Città del Vaticano)",
42374         "va",
42375         "39",
42376         1
42377       ],
42378       [
42379         "Venezuela",
42380         "ve",
42381         "58"
42382       ],
42383       [
42384         "Vietnam (Việt Nam)",
42385         "vn",
42386         "84"
42387       ],
42388       [
42389         "Wallis and Futuna (Wallis-et-Futuna)",
42390         "wf",
42391         "681"
42392       ],
42393       [
42394         "Western Sahara (‫الصحراء الغربية‬‎)",
42395         "eh",
42396         "212",
42397         1
42398       ],
42399       [
42400         "Yemen (‫اليمن‬‎)",
42401         "ye",
42402         "967"
42403       ],
42404       [
42405         "Zambia",
42406         "zm",
42407         "260"
42408       ],
42409       [
42410         "Zimbabwe",
42411         "zw",
42412         "263"
42413       ],
42414       [
42415         "Åland Islands",
42416         "ax",
42417         "358",
42418         1
42419       ]
42420   ];
42421   
42422   return d;
42423 }/**
42424 *    This script refer to:
42425 *    Title: International Telephone Input
42426 *    Author: Jack O'Connor
42427 *    Code version:  v12.1.12
42428 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42429 **/
42430
42431 /**
42432  * @class Roo.bootstrap.PhoneInput
42433  * @extends Roo.bootstrap.TriggerField
42434  * An input with International dial-code selection
42435  
42436  * @cfg {String} defaultDialCode default '+852'
42437  * @cfg {Array} preferedCountries default []
42438   
42439  * @constructor
42440  * Create a new PhoneInput.
42441  * @param {Object} config Configuration options
42442  */
42443
42444 Roo.bootstrap.PhoneInput = function(config) {
42445     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42446 };
42447
42448 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42449         
42450         listWidth: undefined,
42451         
42452         selectedClass: 'active',
42453         
42454         invalidClass : "has-warning",
42455         
42456         validClass: 'has-success',
42457         
42458         allowed: '0123456789',
42459         
42460         max_length: 15,
42461         
42462         /**
42463          * @cfg {String} defaultDialCode The default dial code when initializing the input
42464          */
42465         defaultDialCode: '+852',
42466         
42467         /**
42468          * @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
42469          */
42470         preferedCountries: false,
42471         
42472         getAutoCreate : function()
42473         {
42474             var data = Roo.bootstrap.PhoneInputData();
42475             var align = this.labelAlign || this.parentLabelAlign();
42476             var id = Roo.id();
42477             
42478             this.allCountries = [];
42479             this.dialCodeMapping = [];
42480             
42481             for (var i = 0; i < data.length; i++) {
42482               var c = data[i];
42483               this.allCountries[i] = {
42484                 name: c[0],
42485                 iso2: c[1],
42486                 dialCode: c[2],
42487                 priority: c[3] || 0,
42488                 areaCodes: c[4] || null
42489               };
42490               this.dialCodeMapping[c[2]] = {
42491                   name: c[0],
42492                   iso2: c[1],
42493                   priority: c[3] || 0,
42494                   areaCodes: c[4] || null
42495               };
42496             }
42497             
42498             var cfg = {
42499                 cls: 'form-group',
42500                 cn: []
42501             };
42502             
42503             var input =  {
42504                 tag: 'input',
42505                 id : id,
42506                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42507                 maxlength: this.max_length,
42508                 cls : 'form-control tel-input',
42509                 autocomplete: 'new-password'
42510             };
42511             
42512             var hiddenInput = {
42513                 tag: 'input',
42514                 type: 'hidden',
42515                 cls: 'hidden-tel-input'
42516             };
42517             
42518             if (this.name) {
42519                 hiddenInput.name = this.name;
42520             }
42521             
42522             if (this.disabled) {
42523                 input.disabled = true;
42524             }
42525             
42526             var flag_container = {
42527                 tag: 'div',
42528                 cls: 'flag-box',
42529                 cn: [
42530                     {
42531                         tag: 'div',
42532                         cls: 'flag'
42533                     },
42534                     {
42535                         tag: 'div',
42536                         cls: 'caret'
42537                     }
42538                 ]
42539             };
42540             
42541             var box = {
42542                 tag: 'div',
42543                 cls: this.hasFeedback ? 'has-feedback' : '',
42544                 cn: [
42545                     hiddenInput,
42546                     input,
42547                     {
42548                         tag: 'input',
42549                         cls: 'dial-code-holder',
42550                         disabled: true
42551                     }
42552                 ]
42553             };
42554             
42555             var container = {
42556                 cls: 'roo-select2-container input-group',
42557                 cn: [
42558                     flag_container,
42559                     box
42560                 ]
42561             };
42562             
42563             if (this.fieldLabel.length) {
42564                 var indicator = {
42565                     tag: 'i',
42566                     tooltip: 'This field is required'
42567                 };
42568                 
42569                 var label = {
42570                     tag: 'label',
42571                     'for':  id,
42572                     cls: 'control-label',
42573                     cn: []
42574                 };
42575                 
42576                 var label_text = {
42577                     tag: 'span',
42578                     html: this.fieldLabel
42579                 };
42580                 
42581                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42582                 label.cn = [
42583                     indicator,
42584                     label_text
42585                 ];
42586                 
42587                 if(this.indicatorpos == 'right') {
42588                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42589                     label.cn = [
42590                         label_text,
42591                         indicator
42592                     ];
42593                 }
42594                 
42595                 if(align == 'left') {
42596                     container = {
42597                         tag: 'div',
42598                         cn: [
42599                             container
42600                         ]
42601                     };
42602                     
42603                     if(this.labelWidth > 12){
42604                         label.style = "width: " + this.labelWidth + 'px';
42605                     }
42606                     if(this.labelWidth < 13 && this.labelmd == 0){
42607                         this.labelmd = this.labelWidth;
42608                     }
42609                     if(this.labellg > 0){
42610                         label.cls += ' col-lg-' + this.labellg;
42611                         input.cls += ' col-lg-' + (12 - this.labellg);
42612                     }
42613                     if(this.labelmd > 0){
42614                         label.cls += ' col-md-' + this.labelmd;
42615                         container.cls += ' col-md-' + (12 - this.labelmd);
42616                     }
42617                     if(this.labelsm > 0){
42618                         label.cls += ' col-sm-' + this.labelsm;
42619                         container.cls += ' col-sm-' + (12 - this.labelsm);
42620                     }
42621                     if(this.labelxs > 0){
42622                         label.cls += ' col-xs-' + this.labelxs;
42623                         container.cls += ' col-xs-' + (12 - this.labelxs);
42624                     }
42625                 }
42626             }
42627             
42628             cfg.cn = [
42629                 label,
42630                 container
42631             ];
42632             
42633             var settings = this;
42634             
42635             ['xs','sm','md','lg'].map(function(size){
42636                 if (settings[size]) {
42637                     cfg.cls += ' col-' + size + '-' + settings[size];
42638                 }
42639             });
42640             
42641             this.store = new Roo.data.Store({
42642                 proxy : new Roo.data.MemoryProxy({}),
42643                 reader : new Roo.data.JsonReader({
42644                     fields : [
42645                         {
42646                             'name' : 'name',
42647                             'type' : 'string'
42648                         },
42649                         {
42650                             'name' : 'iso2',
42651                             'type' : 'string'
42652                         },
42653                         {
42654                             'name' : 'dialCode',
42655                             'type' : 'string'
42656                         },
42657                         {
42658                             'name' : 'priority',
42659                             'type' : 'string'
42660                         },
42661                         {
42662                             'name' : 'areaCodes',
42663                             'type' : 'string'
42664                         }
42665                     ]
42666                 })
42667             });
42668             
42669             if(!this.preferedCountries) {
42670                 this.preferedCountries = [
42671                     'hk',
42672                     'gb',
42673                     'us'
42674                 ];
42675             }
42676             
42677             var p = this.preferedCountries.reverse();
42678             
42679             if(p) {
42680                 for (var i = 0; i < p.length; i++) {
42681                     for (var j = 0; j < this.allCountries.length; j++) {
42682                         if(this.allCountries[j].iso2 == p[i]) {
42683                             var t = this.allCountries[j];
42684                             this.allCountries.splice(j,1);
42685                             this.allCountries.unshift(t);
42686                         }
42687                     } 
42688                 }
42689             }
42690             
42691             this.store.proxy.data = {
42692                 success: true,
42693                 data: this.allCountries
42694             };
42695             
42696             return cfg;
42697         },
42698         
42699         initEvents : function()
42700         {
42701             this.createList();
42702             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42703             
42704             this.indicator = this.indicatorEl();
42705             this.flag = this.flagEl();
42706             this.dialCodeHolder = this.dialCodeHolderEl();
42707             
42708             this.trigger = this.el.select('div.flag-box',true).first();
42709             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42710             
42711             var _this = this;
42712             
42713             (function(){
42714                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42715                 _this.list.setWidth(lw);
42716             }).defer(100);
42717             
42718             this.list.on('mouseover', this.onViewOver, this);
42719             this.list.on('mousemove', this.onViewMove, this);
42720             this.inputEl().on("keyup", this.onKeyUp, this);
42721             this.inputEl().on("keypress", this.onKeyPress, this);
42722             
42723             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42724
42725             this.view = new Roo.View(this.list, this.tpl, {
42726                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42727             });
42728             
42729             this.view.on('click', this.onViewClick, this);
42730             this.setValue(this.defaultDialCode);
42731         },
42732         
42733         onTriggerClick : function(e)
42734         {
42735             Roo.log('trigger click');
42736             if(this.disabled){
42737                 return;
42738             }
42739             
42740             if(this.isExpanded()){
42741                 this.collapse();
42742                 this.hasFocus = false;
42743             }else {
42744                 this.store.load({});
42745                 this.hasFocus = true;
42746                 this.expand();
42747             }
42748         },
42749         
42750         isExpanded : function()
42751         {
42752             return this.list.isVisible();
42753         },
42754         
42755         collapse : function()
42756         {
42757             if(!this.isExpanded()){
42758                 return;
42759             }
42760             this.list.hide();
42761             Roo.get(document).un('mousedown', this.collapseIf, this);
42762             Roo.get(document).un('mousewheel', this.collapseIf, this);
42763             this.fireEvent('collapse', this);
42764             this.validate();
42765         },
42766         
42767         expand : function()
42768         {
42769             Roo.log('expand');
42770
42771             if(this.isExpanded() || !this.hasFocus){
42772                 return;
42773             }
42774             
42775             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42776             this.list.setWidth(lw);
42777             
42778             this.list.show();
42779             this.restrictHeight();
42780             
42781             Roo.get(document).on('mousedown', this.collapseIf, this);
42782             Roo.get(document).on('mousewheel', this.collapseIf, this);
42783             
42784             this.fireEvent('expand', this);
42785         },
42786         
42787         restrictHeight : function()
42788         {
42789             this.list.alignTo(this.inputEl(), this.listAlign);
42790             this.list.alignTo(this.inputEl(), this.listAlign);
42791         },
42792         
42793         onViewOver : function(e, t)
42794         {
42795             if(this.inKeyMode){
42796                 return;
42797             }
42798             var item = this.view.findItemFromChild(t);
42799             
42800             if(item){
42801                 var index = this.view.indexOf(item);
42802                 this.select(index, false);
42803             }
42804         },
42805
42806         // private
42807         onViewClick : function(view, doFocus, el, e)
42808         {
42809             var index = this.view.getSelectedIndexes()[0];
42810             
42811             var r = this.store.getAt(index);
42812             
42813             if(r){
42814                 this.onSelect(r, index);
42815             }
42816             if(doFocus !== false && !this.blockFocus){
42817                 this.inputEl().focus();
42818             }
42819         },
42820         
42821         onViewMove : function(e, t)
42822         {
42823             this.inKeyMode = false;
42824         },
42825         
42826         select : function(index, scrollIntoView)
42827         {
42828             this.selectedIndex = index;
42829             this.view.select(index);
42830             if(scrollIntoView !== false){
42831                 var el = this.view.getNode(index);
42832                 if(el){
42833                     this.list.scrollChildIntoView(el, false);
42834                 }
42835             }
42836         },
42837         
42838         createList : function()
42839         {
42840             this.list = Roo.get(document.body).createChild({
42841                 tag: 'ul',
42842                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42843                 style: 'display:none'
42844             });
42845             
42846             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42847         },
42848         
42849         collapseIf : function(e)
42850         {
42851             var in_combo  = e.within(this.el);
42852             var in_list =  e.within(this.list);
42853             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42854             
42855             if (in_combo || in_list || is_list) {
42856                 return;
42857             }
42858             this.collapse();
42859         },
42860         
42861         onSelect : function(record, index)
42862         {
42863             if(this.fireEvent('beforeselect', this, record, index) !== false){
42864                 
42865                 this.setFlagClass(record.data.iso2);
42866                 this.setDialCode(record.data.dialCode);
42867                 this.hasFocus = false;
42868                 this.collapse();
42869                 this.fireEvent('select', this, record, index);
42870             }
42871         },
42872         
42873         flagEl : function()
42874         {
42875             var flag = this.el.select('div.flag',true).first();
42876             if(!flag){
42877                 return false;
42878             }
42879             return flag;
42880         },
42881         
42882         dialCodeHolderEl : function()
42883         {
42884             var d = this.el.select('input.dial-code-holder',true).first();
42885             if(!d){
42886                 return false;
42887             }
42888             return d;
42889         },
42890         
42891         setDialCode : function(v)
42892         {
42893             this.dialCodeHolder.dom.value = '+'+v;
42894         },
42895         
42896         setFlagClass : function(n)
42897         {
42898             this.flag.dom.className = 'flag '+n;
42899         },
42900         
42901         getValue : function()
42902         {
42903             var v = this.inputEl().getValue();
42904             if(this.dialCodeHolder) {
42905                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42906             }
42907             return v;
42908         },
42909         
42910         setValue : function(v)
42911         {
42912             var d = this.getDialCode(v);
42913             
42914             //invalid dial code
42915             if(v.length == 0 || !d || d.length == 0) {
42916                 if(this.rendered){
42917                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42918                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42919                 }
42920                 return;
42921             }
42922             
42923             //valid dial code
42924             this.setFlagClass(this.dialCodeMapping[d].iso2);
42925             this.setDialCode(d);
42926             this.inputEl().dom.value = v.replace('+'+d,'');
42927             this.hiddenEl().dom.value = this.getValue();
42928             
42929             this.validate();
42930         },
42931         
42932         getDialCode : function(v)
42933         {
42934             v = v ||  '';
42935             
42936             if (v.length == 0) {
42937                 return this.dialCodeHolder.dom.value;
42938             }
42939             
42940             var dialCode = "";
42941             if (v.charAt(0) != "+") {
42942                 return false;
42943             }
42944             var numericChars = "";
42945             for (var i = 1; i < v.length; i++) {
42946               var c = v.charAt(i);
42947               if (!isNaN(c)) {
42948                 numericChars += c;
42949                 if (this.dialCodeMapping[numericChars]) {
42950                   dialCode = v.substr(1, i);
42951                 }
42952                 if (numericChars.length == 4) {
42953                   break;
42954                 }
42955               }
42956             }
42957             return dialCode;
42958         },
42959         
42960         reset : function()
42961         {
42962             this.setValue(this.defaultDialCode);
42963             this.validate();
42964         },
42965         
42966         hiddenEl : function()
42967         {
42968             return this.el.select('input.hidden-tel-input',true).first();
42969         },
42970         
42971         // after setting val
42972         onKeyUp : function(e){
42973             this.setValue(this.getValue());
42974         },
42975         
42976         onKeyPress : function(e){
42977             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42978                 e.stopEvent();
42979             }
42980         }
42981         
42982 });
42983 /**
42984  * @class Roo.bootstrap.MoneyField
42985  * @extends Roo.bootstrap.ComboBox
42986  * Bootstrap MoneyField class
42987  * 
42988  * @constructor
42989  * Create a new MoneyField.
42990  * @param {Object} config Configuration options
42991  */
42992
42993 Roo.bootstrap.MoneyField = function(config) {
42994     
42995     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42996     
42997 };
42998
42999 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43000     
43001     /**
43002      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43003      */
43004     allowDecimals : true,
43005     /**
43006      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43007      */
43008     decimalSeparator : ".",
43009     /**
43010      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43011      */
43012     decimalPrecision : 0,
43013     /**
43014      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43015      */
43016     allowNegative : true,
43017     /**
43018      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43019      */
43020     allowZero: true,
43021     /**
43022      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43023      */
43024     minValue : Number.NEGATIVE_INFINITY,
43025     /**
43026      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43027      */
43028     maxValue : Number.MAX_VALUE,
43029     /**
43030      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43031      */
43032     minText : "The minimum value for this field is {0}",
43033     /**
43034      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43035      */
43036     maxText : "The maximum value for this field is {0}",
43037     /**
43038      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43039      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43040      */
43041     nanText : "{0} is not a valid number",
43042     /**
43043      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43044      */
43045     castInt : true,
43046     /**
43047      * @cfg {String} defaults currency of the MoneyField
43048      * value should be in lkey
43049      */
43050     defaultCurrency : false,
43051     /**
43052      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43053      */
43054     thousandsDelimiter : false,
43055     /**
43056      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43057      */
43058     max_length: false,
43059     
43060     inputlg : 9,
43061     inputmd : 9,
43062     inputsm : 9,
43063     inputxs : 6,
43064     
43065     store : false,
43066     
43067     getAutoCreate : function()
43068     {
43069         var align = this.labelAlign || this.parentLabelAlign();
43070         
43071         var id = Roo.id();
43072
43073         var cfg = {
43074             cls: 'form-group',
43075             cn: []
43076         };
43077
43078         var input =  {
43079             tag: 'input',
43080             id : id,
43081             cls : 'form-control roo-money-amount-input',
43082             autocomplete: 'new-password'
43083         };
43084         
43085         var hiddenInput = {
43086             tag: 'input',
43087             type: 'hidden',
43088             id: Roo.id(),
43089             cls: 'hidden-number-input'
43090         };
43091         
43092         if(this.max_length) {
43093             input.maxlength = this.max_length; 
43094         }
43095         
43096         if (this.name) {
43097             hiddenInput.name = this.name;
43098         }
43099
43100         if (this.disabled) {
43101             input.disabled = true;
43102         }
43103
43104         var clg = 12 - this.inputlg;
43105         var cmd = 12 - this.inputmd;
43106         var csm = 12 - this.inputsm;
43107         var cxs = 12 - this.inputxs;
43108         
43109         var container = {
43110             tag : 'div',
43111             cls : 'row roo-money-field',
43112             cn : [
43113                 {
43114                     tag : 'div',
43115                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43116                     cn : [
43117                         {
43118                             tag : 'div',
43119                             cls: 'roo-select2-container input-group',
43120                             cn: [
43121                                 {
43122                                     tag : 'input',
43123                                     cls : 'form-control roo-money-currency-input',
43124                                     autocomplete: 'new-password',
43125                                     readOnly : 1,
43126                                     name : this.currencyName
43127                                 },
43128                                 {
43129                                     tag :'span',
43130                                     cls : 'input-group-addon',
43131                                     cn : [
43132                                         {
43133                                             tag: 'span',
43134                                             cls: 'caret'
43135                                         }
43136                                     ]
43137                                 }
43138                             ]
43139                         }
43140                     ]
43141                 },
43142                 {
43143                     tag : 'div',
43144                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43145                     cn : [
43146                         {
43147                             tag: 'div',
43148                             cls: this.hasFeedback ? 'has-feedback' : '',
43149                             cn: [
43150                                 input
43151                             ]
43152                         }
43153                     ]
43154                 }
43155             ]
43156             
43157         };
43158         
43159         if (this.fieldLabel.length) {
43160             var indicator = {
43161                 tag: 'i',
43162                 tooltip: 'This field is required'
43163             };
43164
43165             var label = {
43166                 tag: 'label',
43167                 'for':  id,
43168                 cls: 'control-label',
43169                 cn: []
43170             };
43171
43172             var label_text = {
43173                 tag: 'span',
43174                 html: this.fieldLabel
43175             };
43176
43177             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43178             label.cn = [
43179                 indicator,
43180                 label_text
43181             ];
43182
43183             if(this.indicatorpos == 'right') {
43184                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43185                 label.cn = [
43186                     label_text,
43187                     indicator
43188                 ];
43189             }
43190
43191             if(align == 'left') {
43192                 container = {
43193                     tag: 'div',
43194                     cn: [
43195                         container
43196                     ]
43197                 };
43198
43199                 if(this.labelWidth > 12){
43200                     label.style = "width: " + this.labelWidth + 'px';
43201                 }
43202                 if(this.labelWidth < 13 && this.labelmd == 0){
43203                     this.labelmd = this.labelWidth;
43204                 }
43205                 if(this.labellg > 0){
43206                     label.cls += ' col-lg-' + this.labellg;
43207                     input.cls += ' col-lg-' + (12 - this.labellg);
43208                 }
43209                 if(this.labelmd > 0){
43210                     label.cls += ' col-md-' + this.labelmd;
43211                     container.cls += ' col-md-' + (12 - this.labelmd);
43212                 }
43213                 if(this.labelsm > 0){
43214                     label.cls += ' col-sm-' + this.labelsm;
43215                     container.cls += ' col-sm-' + (12 - this.labelsm);
43216                 }
43217                 if(this.labelxs > 0){
43218                     label.cls += ' col-xs-' + this.labelxs;
43219                     container.cls += ' col-xs-' + (12 - this.labelxs);
43220                 }
43221             }
43222         }
43223
43224         cfg.cn = [
43225             label,
43226             container,
43227             hiddenInput
43228         ];
43229         
43230         var settings = this;
43231
43232         ['xs','sm','md','lg'].map(function(size){
43233             if (settings[size]) {
43234                 cfg.cls += ' col-' + size + '-' + settings[size];
43235             }
43236         });
43237         
43238         return cfg;
43239     },
43240     
43241     initEvents : function()
43242     {
43243         this.indicator = this.indicatorEl();
43244         
43245         this.initCurrencyEvent();
43246         
43247         this.initNumberEvent();
43248     },
43249     
43250     initCurrencyEvent : function()
43251     {
43252         if (!this.store) {
43253             throw "can not find store for combo";
43254         }
43255         
43256         this.store = Roo.factory(this.store, Roo.data);
43257         this.store.parent = this;
43258         
43259         this.createList();
43260         
43261         this.triggerEl = this.el.select('.input-group-addon', true).first();
43262         
43263         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43264         
43265         var _this = this;
43266         
43267         (function(){
43268             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43269             _this.list.setWidth(lw);
43270         }).defer(100);
43271         
43272         this.list.on('mouseover', this.onViewOver, this);
43273         this.list.on('mousemove', this.onViewMove, this);
43274         this.list.on('scroll', this.onViewScroll, this);
43275         
43276         if(!this.tpl){
43277             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43278         }
43279         
43280         this.view = new Roo.View(this.list, this.tpl, {
43281             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43282         });
43283         
43284         this.view.on('click', this.onViewClick, this);
43285         
43286         this.store.on('beforeload', this.onBeforeLoad, this);
43287         this.store.on('load', this.onLoad, this);
43288         this.store.on('loadexception', this.onLoadException, this);
43289         
43290         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43291             "up" : function(e){
43292                 this.inKeyMode = true;
43293                 this.selectPrev();
43294             },
43295
43296             "down" : function(e){
43297                 if(!this.isExpanded()){
43298                     this.onTriggerClick();
43299                 }else{
43300                     this.inKeyMode = true;
43301                     this.selectNext();
43302                 }
43303             },
43304
43305             "enter" : function(e){
43306                 this.collapse();
43307                 
43308                 if(this.fireEvent("specialkey", this, e)){
43309                     this.onViewClick(false);
43310                 }
43311                 
43312                 return true;
43313             },
43314
43315             "esc" : function(e){
43316                 this.collapse();
43317             },
43318
43319             "tab" : function(e){
43320                 this.collapse();
43321                 
43322                 if(this.fireEvent("specialkey", this, e)){
43323                     this.onViewClick(false);
43324                 }
43325                 
43326                 return true;
43327             },
43328
43329             scope : this,
43330
43331             doRelay : function(foo, bar, hname){
43332                 if(hname == 'down' || this.scope.isExpanded()){
43333                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43334                 }
43335                 return true;
43336             },
43337
43338             forceKeyDown: true
43339         });
43340         
43341         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43342         
43343     },
43344     
43345     initNumberEvent : function(e)
43346     {
43347         this.inputEl().on("keydown" , this.fireKey,  this);
43348         this.inputEl().on("focus", this.onFocus,  this);
43349         this.inputEl().on("blur", this.onBlur,  this);
43350         
43351         this.inputEl().relayEvent('keyup', this);
43352         
43353         if(this.indicator){
43354             this.indicator.addClass('invisible');
43355         }
43356  
43357         this.originalValue = this.getValue();
43358         
43359         if(this.validationEvent == 'keyup'){
43360             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43361             this.inputEl().on('keyup', this.filterValidation, this);
43362         }
43363         else if(this.validationEvent !== false){
43364             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43365         }
43366         
43367         if(this.selectOnFocus){
43368             this.on("focus", this.preFocus, this);
43369             
43370         }
43371         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43372             this.inputEl().on("keypress", this.filterKeys, this);
43373         } else {
43374             this.inputEl().relayEvent('keypress', this);
43375         }
43376         
43377         var allowed = "0123456789";
43378         
43379         if(this.allowDecimals){
43380             allowed += this.decimalSeparator;
43381         }
43382         
43383         if(this.allowNegative){
43384             allowed += "-";
43385         }
43386         
43387         if(this.thousandsDelimiter) {
43388             allowed += ",";
43389         }
43390         
43391         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43392         
43393         var keyPress = function(e){
43394             
43395             var k = e.getKey();
43396             
43397             var c = e.getCharCode();
43398             
43399             if(
43400                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43401                     allowed.indexOf(String.fromCharCode(c)) === -1
43402             ){
43403                 e.stopEvent();
43404                 return;
43405             }
43406             
43407             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43408                 return;
43409             }
43410             
43411             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43412                 e.stopEvent();
43413             }
43414         };
43415         
43416         this.inputEl().on("keypress", keyPress, this);
43417         
43418     },
43419     
43420     onTriggerClick : function(e)
43421     {   
43422         if(this.disabled){
43423             return;
43424         }
43425         
43426         this.page = 0;
43427         this.loadNext = false;
43428         
43429         if(this.isExpanded()){
43430             this.collapse();
43431             return;
43432         }
43433         
43434         this.hasFocus = true;
43435         
43436         if(this.triggerAction == 'all') {
43437             this.doQuery(this.allQuery, true);
43438             return;
43439         }
43440         
43441         this.doQuery(this.getRawValue());
43442     },
43443     
43444     getCurrency : function()
43445     {   
43446         var v = this.currencyEl().getValue();
43447         
43448         return v;
43449     },
43450     
43451     restrictHeight : function()
43452     {
43453         this.list.alignTo(this.currencyEl(), this.listAlign);
43454         this.list.alignTo(this.currencyEl(), this.listAlign);
43455     },
43456     
43457     onViewClick : function(view, doFocus, el, e)
43458     {
43459         var index = this.view.getSelectedIndexes()[0];
43460         
43461         var r = this.store.getAt(index);
43462         
43463         if(r){
43464             this.onSelect(r, index);
43465         }
43466     },
43467     
43468     onSelect : function(record, index){
43469         
43470         if(this.fireEvent('beforeselect', this, record, index) !== false){
43471         
43472             this.setFromCurrencyData(index > -1 ? record.data : false);
43473             
43474             this.collapse();
43475             
43476             this.fireEvent('select', this, record, index);
43477         }
43478     },
43479     
43480     setFromCurrencyData : function(o)
43481     {
43482         var currency = '';
43483         
43484         this.lastCurrency = o;
43485         
43486         if (this.currencyField) {
43487             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43488         } else {
43489             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43490         }
43491         
43492         this.lastSelectionText = currency;
43493         
43494         //setting default currency
43495         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43496             this.setCurrency(this.defaultCurrency);
43497             return;
43498         }
43499         
43500         this.setCurrency(currency);
43501     },
43502     
43503     setFromData : function(o)
43504     {
43505         var c = {};
43506         
43507         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43508         
43509         this.setFromCurrencyData(c);
43510         
43511         var value = '';
43512         
43513         if (this.name) {
43514             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43515         } else {
43516             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43517         }
43518         
43519         this.setValue(value);
43520         
43521     },
43522     
43523     setCurrency : function(v)
43524     {   
43525         this.currencyValue = v;
43526         
43527         if(this.rendered){
43528             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43529             this.validate();
43530         }
43531     },
43532     
43533     setValue : function(v)
43534     {
43535         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43536         
43537         this.value = v;
43538         
43539         if(this.rendered){
43540             
43541             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43542             
43543             this.inputEl().dom.value = (v == '') ? '' :
43544                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43545             
43546             if(!this.allowZero && v === '0') {
43547                 this.hiddenEl().dom.value = '';
43548                 this.inputEl().dom.value = '';
43549             }
43550             
43551             this.validate();
43552         }
43553     },
43554     
43555     getRawValue : function()
43556     {
43557         var v = this.inputEl().getValue();
43558         
43559         return v;
43560     },
43561     
43562     getValue : function()
43563     {
43564         return this.fixPrecision(this.parseValue(this.getRawValue()));
43565     },
43566     
43567     parseValue : function(value)
43568     {
43569         if(this.thousandsDelimiter) {
43570             value += "";
43571             r = new RegExp(",", "g");
43572             value = value.replace(r, "");
43573         }
43574         
43575         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43576         return isNaN(value) ? '' : value;
43577         
43578     },
43579     
43580     fixPrecision : function(value)
43581     {
43582         if(this.thousandsDelimiter) {
43583             value += "";
43584             r = new RegExp(",", "g");
43585             value = value.replace(r, "");
43586         }
43587         
43588         var nan = isNaN(value);
43589         
43590         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43591             return nan ? '' : value;
43592         }
43593         return parseFloat(value).toFixed(this.decimalPrecision);
43594     },
43595     
43596     decimalPrecisionFcn : function(v)
43597     {
43598         return Math.floor(v);
43599     },
43600     
43601     validateValue : function(value)
43602     {
43603         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43604             return false;
43605         }
43606         
43607         var num = this.parseValue(value);
43608         
43609         if(isNaN(num)){
43610             this.markInvalid(String.format(this.nanText, value));
43611             return false;
43612         }
43613         
43614         if(num < this.minValue){
43615             this.markInvalid(String.format(this.minText, this.minValue));
43616             return false;
43617         }
43618         
43619         if(num > this.maxValue){
43620             this.markInvalid(String.format(this.maxText, this.maxValue));
43621             return false;
43622         }
43623         
43624         return true;
43625     },
43626     
43627     validate : function()
43628     {
43629         if(this.disabled || this.allowBlank){
43630             this.markValid();
43631             return true;
43632         }
43633         
43634         var currency = this.getCurrency();
43635         
43636         if(this.validateValue(this.getRawValue()) && currency.length){
43637             this.markValid();
43638             return true;
43639         }
43640         
43641         this.markInvalid();
43642         return false;
43643     },
43644     
43645     getName: function()
43646     {
43647         return this.name;
43648     },
43649     
43650     beforeBlur : function()
43651     {
43652         if(!this.castInt){
43653             return;
43654         }
43655         
43656         var v = this.parseValue(this.getRawValue());
43657         
43658         if(v || v == 0){
43659             this.setValue(v);
43660         }
43661     },
43662     
43663     onBlur : function()
43664     {
43665         this.beforeBlur();
43666         
43667         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43668             //this.el.removeClass(this.focusClass);
43669         }
43670         
43671         this.hasFocus = false;
43672         
43673         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43674             this.validate();
43675         }
43676         
43677         var v = this.getValue();
43678         
43679         if(String(v) !== String(this.startValue)){
43680             this.fireEvent('change', this, v, this.startValue);
43681         }
43682         
43683         this.fireEvent("blur", this);
43684     },
43685     
43686     inputEl : function()
43687     {
43688         return this.el.select('.roo-money-amount-input', true).first();
43689     },
43690     
43691     currencyEl : function()
43692     {
43693         return this.el.select('.roo-money-currency-input', true).first();
43694     },
43695     
43696     hiddenEl : function()
43697     {
43698         return this.el.select('input.hidden-number-input',true).first();
43699     }
43700     
43701 });/**
43702  * @class Roo.bootstrap.BezierSignature
43703  * @extends Roo.bootstrap.Component
43704  * Bootstrap BezierSignature class
43705  * This script refer to:
43706  *    Title: Signature Pad
43707  *    Author: szimek
43708  *    Availability: https://github.com/szimek/signature_pad
43709  *
43710  * @constructor
43711  * Create a new BezierSignature
43712  * @param {Object} config The config object
43713  */
43714
43715 Roo.bootstrap.BezierSignature = function(config){
43716     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43717     this.addEvents({
43718         "resize" : true
43719     });
43720 };
43721
43722 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43723 {
43724      
43725     curve_data: [],
43726     
43727     is_empty: true,
43728     
43729     mouse_btn_down: true,
43730     
43731     /**
43732      * @cfg {int} canvas height
43733      */
43734     canvas_height: '200px',
43735     
43736     /**
43737      * @cfg {float|function} Radius of a single dot.
43738      */ 
43739     dot_size: false,
43740     
43741     /**
43742      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43743      */
43744     min_width: 0.5,
43745     
43746     /**
43747      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43748      */
43749     max_width: 2.5,
43750     
43751     /**
43752      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43753      */
43754     throttle: 16,
43755     
43756     /**
43757      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43758      */
43759     min_distance: 5,
43760     
43761     /**
43762      * @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.
43763      */
43764     bg_color: 'rgba(0, 0, 0, 0)',
43765     
43766     /**
43767      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43768      */
43769     dot_color: 'black',
43770     
43771     /**
43772      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43773      */ 
43774     velocity_filter_weight: 0.7,
43775     
43776     /**
43777      * @cfg {function} Callback when stroke begin. 
43778      */
43779     onBegin: false,
43780     
43781     /**
43782      * @cfg {function} Callback when stroke end.
43783      */
43784     onEnd: false,
43785     
43786     getAutoCreate : function()
43787     {
43788         var cls = 'roo-signature column';
43789         
43790         if(this.cls){
43791             cls += ' ' + this.cls;
43792         }
43793         
43794         var col_sizes = [
43795             'lg',
43796             'md',
43797             'sm',
43798             'xs'
43799         ];
43800         
43801         for(var i = 0; i < col_sizes.length; i++) {
43802             if(this[col_sizes[i]]) {
43803                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43804             }
43805         }
43806         
43807         var cfg = {
43808             tag: 'div',
43809             cls: cls,
43810             cn: [
43811                 {
43812                     tag: 'div',
43813                     cls: 'roo-signature-body',
43814                     cn: [
43815                         {
43816                             tag: 'canvas',
43817                             cls: 'roo-signature-body-canvas',
43818                             height: this.canvas_height,
43819                             width: this.canvas_width
43820                         }
43821                     ]
43822                 },
43823                 {
43824                     tag: 'input',
43825                     type: 'file',
43826                     style: 'display: none'
43827                 }
43828             ]
43829         };
43830         
43831         return cfg;
43832     },
43833     
43834     initEvents: function() 
43835     {
43836         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43837         
43838         var canvas = this.canvasEl();
43839         
43840         // mouse && touch event swapping...
43841         canvas.dom.style.touchAction = 'none';
43842         canvas.dom.style.msTouchAction = 'none';
43843         
43844         this.mouse_btn_down = false;
43845         canvas.on('mousedown', this._handleMouseDown, this);
43846         canvas.on('mousemove', this._handleMouseMove, this);
43847         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43848         
43849         if (window.PointerEvent) {
43850             canvas.on('pointerdown', this._handleMouseDown, this);
43851             canvas.on('pointermove', this._handleMouseMove, this);
43852             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43853         }
43854         
43855         if ('ontouchstart' in window) {
43856             canvas.on('touchstart', this._handleTouchStart, this);
43857             canvas.on('touchmove', this._handleTouchMove, this);
43858             canvas.on('touchend', this._handleTouchEnd, this);
43859         }
43860         
43861         Roo.EventManager.onWindowResize(this.resize, this, true);
43862         
43863         // file input event
43864         this.fileEl().on('change', this.uploadImage, this);
43865         
43866         this.clear();
43867         
43868         this.resize();
43869     },
43870     
43871     resize: function(){
43872         
43873         var canvas = this.canvasEl().dom;
43874         var ctx = this.canvasElCtx();
43875         var img_data = false;
43876         
43877         if(canvas.width > 0) {
43878             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43879         }
43880         // setting canvas width will clean img data
43881         canvas.width = 0;
43882         
43883         var style = window.getComputedStyle ? 
43884             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43885             
43886         var padding_left = parseInt(style.paddingLeft) || 0;
43887         var padding_right = parseInt(style.paddingRight) || 0;
43888         
43889         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43890         
43891         if(img_data) {
43892             ctx.putImageData(img_data, 0, 0);
43893         }
43894     },
43895     
43896     _handleMouseDown: function(e)
43897     {
43898         if (e.browserEvent.which === 1) {
43899             this.mouse_btn_down = true;
43900             this.strokeBegin(e);
43901         }
43902     },
43903     
43904     _handleMouseMove: function (e)
43905     {
43906         if (this.mouse_btn_down) {
43907             this.strokeMoveUpdate(e);
43908         }
43909     },
43910     
43911     _handleMouseUp: function (e)
43912     {
43913         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43914             this.mouse_btn_down = false;
43915             this.strokeEnd(e);
43916         }
43917     },
43918     
43919     _handleTouchStart: function (e) {
43920         
43921         e.preventDefault();
43922         if (e.browserEvent.targetTouches.length === 1) {
43923             // var touch = e.browserEvent.changedTouches[0];
43924             // this.strokeBegin(touch);
43925             
43926              this.strokeBegin(e); // assume e catching the correct xy...
43927         }
43928     },
43929     
43930     _handleTouchMove: function (e) {
43931         e.preventDefault();
43932         // var touch = event.targetTouches[0];
43933         // _this._strokeMoveUpdate(touch);
43934         this.strokeMoveUpdate(e);
43935     },
43936     
43937     _handleTouchEnd: function (e) {
43938         var wasCanvasTouched = e.target === this.canvasEl().dom;
43939         if (wasCanvasTouched) {
43940             e.preventDefault();
43941             // var touch = event.changedTouches[0];
43942             // _this._strokeEnd(touch);
43943             this.strokeEnd(e);
43944         }
43945     },
43946     
43947     reset: function () {
43948         this._lastPoints = [];
43949         this._lastVelocity = 0;
43950         this._lastWidth = (this.min_width + this.max_width) / 2;
43951         this.canvasElCtx().fillStyle = this.dot_color;
43952     },
43953     
43954     strokeMoveUpdate: function(e)
43955     {
43956         this.strokeUpdate(e);
43957         
43958         if (this.throttle) {
43959             this.throttleStroke(this.strokeUpdate, this.throttle);
43960         }
43961         else {
43962             this.strokeUpdate(e);
43963         }
43964     },
43965     
43966     strokeBegin: function(e)
43967     {
43968         var newPointGroup = {
43969             color: this.dot_color,
43970             points: []
43971         };
43972         
43973         if (typeof this.onBegin === 'function') {
43974             this.onBegin(e);
43975         }
43976         
43977         this.curve_data.push(newPointGroup);
43978         this.reset();
43979         this.strokeUpdate(e);
43980     },
43981     
43982     strokeUpdate: function(e)
43983     {
43984         var rect = this.canvasEl().dom.getBoundingClientRect();
43985         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43986         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43987         var lastPoints = lastPointGroup.points;
43988         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43989         var isLastPointTooClose = lastPoint
43990             ? point.distanceTo(lastPoint) <= this.min_distance
43991             : false;
43992         var color = lastPointGroup.color;
43993         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43994             var curve = this.addPoint(point);
43995             if (!lastPoint) {
43996                 this.drawDot({color: color, point: point});
43997             }
43998             else if (curve) {
43999                 this.drawCurve({color: color, curve: curve});
44000             }
44001             lastPoints.push({
44002                 time: point.time,
44003                 x: point.x,
44004                 y: point.y
44005             });
44006         }
44007     },
44008     
44009     strokeEnd: function(e)
44010     {
44011         this.strokeUpdate(e);
44012         if (typeof this.onEnd === 'function') {
44013             this.onEnd(e);
44014         }
44015     },
44016     
44017     addPoint:  function (point) {
44018         var _lastPoints = this._lastPoints;
44019         _lastPoints.push(point);
44020         if (_lastPoints.length > 2) {
44021             if (_lastPoints.length === 3) {
44022                 _lastPoints.unshift(_lastPoints[0]);
44023             }
44024             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44025             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44026             _lastPoints.shift();
44027             return curve;
44028         }
44029         return null;
44030     },
44031     
44032     calculateCurveWidths: function (startPoint, endPoint) {
44033         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44034             (1 - this.velocity_filter_weight) * this._lastVelocity;
44035
44036         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44037         var widths = {
44038             end: newWidth,
44039             start: this._lastWidth
44040         };
44041         
44042         this._lastVelocity = velocity;
44043         this._lastWidth = newWidth;
44044         return widths;
44045     },
44046     
44047     drawDot: function (_a) {
44048         var color = _a.color, point = _a.point;
44049         var ctx = this.canvasElCtx();
44050         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44051         ctx.beginPath();
44052         this.drawCurveSegment(point.x, point.y, width);
44053         ctx.closePath();
44054         ctx.fillStyle = color;
44055         ctx.fill();
44056     },
44057     
44058     drawCurve: function (_a) {
44059         var color = _a.color, curve = _a.curve;
44060         var ctx = this.canvasElCtx();
44061         var widthDelta = curve.endWidth - curve.startWidth;
44062         var drawSteps = Math.floor(curve.length()) * 2;
44063         ctx.beginPath();
44064         ctx.fillStyle = color;
44065         for (var i = 0; i < drawSteps; i += 1) {
44066         var t = i / drawSteps;
44067         var tt = t * t;
44068         var ttt = tt * t;
44069         var u = 1 - t;
44070         var uu = u * u;
44071         var uuu = uu * u;
44072         var x = uuu * curve.startPoint.x;
44073         x += 3 * uu * t * curve.control1.x;
44074         x += 3 * u * tt * curve.control2.x;
44075         x += ttt * curve.endPoint.x;
44076         var y = uuu * curve.startPoint.y;
44077         y += 3 * uu * t * curve.control1.y;
44078         y += 3 * u * tt * curve.control2.y;
44079         y += ttt * curve.endPoint.y;
44080         var width = curve.startWidth + ttt * widthDelta;
44081         this.drawCurveSegment(x, y, width);
44082         }
44083         ctx.closePath();
44084         ctx.fill();
44085     },
44086     
44087     drawCurveSegment: function (x, y, width) {
44088         var ctx = this.canvasElCtx();
44089         ctx.moveTo(x, y);
44090         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44091         this.is_empty = false;
44092     },
44093     
44094     clear: function()
44095     {
44096         var ctx = this.canvasElCtx();
44097         var canvas = this.canvasEl().dom;
44098         ctx.fillStyle = this.bg_color;
44099         ctx.clearRect(0, 0, canvas.width, canvas.height);
44100         ctx.fillRect(0, 0, canvas.width, canvas.height);
44101         this.curve_data = [];
44102         this.reset();
44103         this.is_empty = true;
44104     },
44105     
44106     fileEl: function()
44107     {
44108         return  this.el.select('input',true).first();
44109     },
44110     
44111     canvasEl: function()
44112     {
44113         return this.el.select('canvas',true).first();
44114     },
44115     
44116     canvasElCtx: function()
44117     {
44118         return this.el.select('canvas',true).first().dom.getContext('2d');
44119     },
44120     
44121     getImage: function(type)
44122     {
44123         if(this.is_empty) {
44124             return false;
44125         }
44126         
44127         // encryption ?
44128         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44129     },
44130     
44131     drawFromImage: function(img_src)
44132     {
44133         var img = new Image();
44134         
44135         img.onload = function(){
44136             this.canvasElCtx().drawImage(img, 0, 0);
44137         }.bind(this);
44138         
44139         img.src = img_src;
44140         
44141         this.is_empty = false;
44142     },
44143     
44144     selectImage: function()
44145     {
44146         this.fileEl().dom.click();
44147     },
44148     
44149     uploadImage: function(e)
44150     {
44151         var reader = new FileReader();
44152         
44153         reader.onload = function(e){
44154             var img = new Image();
44155             img.onload = function(){
44156                 this.reset();
44157                 this.canvasElCtx().drawImage(img, 0, 0);
44158             }.bind(this);
44159             img.src = e.target.result;
44160         }.bind(this);
44161         
44162         reader.readAsDataURL(e.target.files[0]);
44163     },
44164     
44165     // Bezier Point Constructor
44166     Point: (function () {
44167         function Point(x, y, time) {
44168             this.x = x;
44169             this.y = y;
44170             this.time = time || Date.now();
44171         }
44172         Point.prototype.distanceTo = function (start) {
44173             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44174         };
44175         Point.prototype.equals = function (other) {
44176             return this.x === other.x && this.y === other.y && this.time === other.time;
44177         };
44178         Point.prototype.velocityFrom = function (start) {
44179             return this.time !== start.time
44180             ? this.distanceTo(start) / (this.time - start.time)
44181             : 0;
44182         };
44183         return Point;
44184     }()),
44185     
44186     
44187     // Bezier Constructor
44188     Bezier: (function () {
44189         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44190             this.startPoint = startPoint;
44191             this.control2 = control2;
44192             this.control1 = control1;
44193             this.endPoint = endPoint;
44194             this.startWidth = startWidth;
44195             this.endWidth = endWidth;
44196         }
44197         Bezier.fromPoints = function (points, widths, scope) {
44198             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44199             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44200             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44201         };
44202         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44203             var dx1 = s1.x - s2.x;
44204             var dy1 = s1.y - s2.y;
44205             var dx2 = s2.x - s3.x;
44206             var dy2 = s2.y - s3.y;
44207             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44208             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44209             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44210             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44211             var dxm = m1.x - m2.x;
44212             var dym = m1.y - m2.y;
44213             var k = l2 / (l1 + l2);
44214             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44215             var tx = s2.x - cm.x;
44216             var ty = s2.y - cm.y;
44217             return {
44218                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44219                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44220             };
44221         };
44222         Bezier.prototype.length = function () {
44223             var steps = 10;
44224             var length = 0;
44225             var px;
44226             var py;
44227             for (var i = 0; i <= steps; i += 1) {
44228                 var t = i / steps;
44229                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44230                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44231                 if (i > 0) {
44232                     var xdiff = cx - px;
44233                     var ydiff = cy - py;
44234                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44235                 }
44236                 px = cx;
44237                 py = cy;
44238             }
44239             return length;
44240         };
44241         Bezier.prototype.point = function (t, start, c1, c2, end) {
44242             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44243             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44244             + (3.0 * c2 * (1.0 - t) * t * t)
44245             + (end * t * t * t);
44246         };
44247         return Bezier;
44248     }()),
44249     
44250     throttleStroke: function(fn, wait) {
44251       if (wait === void 0) { wait = 250; }
44252       var previous = 0;
44253       var timeout = null;
44254       var result;
44255       var storedContext;
44256       var storedArgs;
44257       var later = function () {
44258           previous = Date.now();
44259           timeout = null;
44260           result = fn.apply(storedContext, storedArgs);
44261           if (!timeout) {
44262               storedContext = null;
44263               storedArgs = [];
44264           }
44265       };
44266       return function wrapper() {
44267           var args = [];
44268           for (var _i = 0; _i < arguments.length; _i++) {
44269               args[_i] = arguments[_i];
44270           }
44271           var now = Date.now();
44272           var remaining = wait - (now - previous);
44273           storedContext = this;
44274           storedArgs = args;
44275           if (remaining <= 0 || remaining > wait) {
44276               if (timeout) {
44277                   clearTimeout(timeout);
44278                   timeout = null;
44279               }
44280               previous = now;
44281               result = fn.apply(storedContext, storedArgs);
44282               if (!timeout) {
44283                   storedContext = null;
44284                   storedArgs = [];
44285               }
44286           }
44287           else if (!timeout) {
44288               timeout = window.setTimeout(later, remaining);
44289           }
44290           return result;
44291       };
44292   }
44293   
44294 });
44295
44296  
44297
44298